Jump to content

Josh

Staff
  • Posts

    23,289
  • Joined

  • Last visited

Blog Entries posted by Josh

  1. Josh
    A new build is available on the beta branch on Steam.
    Fixed Kinematic joint rotation (thanks Julio!) Fixed FlushKeys / FlushMouse not working on Linux. I'm still having trouble compiling with the latest version of Code::Blocks / GCC:
    https://www.leadwerks.com/community/topic/14663-compile-problem-on-ubuntu-1604-using-codeblocks-c
     
  2. Josh
    The model editor animation bug was the second-worst bug to hit Leadwerks Game Engine in all its history. Reported multiple times, this would cause animated models to discard triangles only in the model editor, only on Linux.
    http://www.leadwerks.com/werkspace/topic/10856-model-editor-freaks-out/
    http://www.leadwerks.com/werkspace/topic/12678-model-animation-vs-flashing-bodyparts/

    Since our animation commands have worked solidly for years, I was at my wits' end trying to figure this out. I strongly suspected a driver bug having to do with sharing uniform buffers across multiple contexts, but the fact it happened on both AMD and Nvidia cards did not support that, or indicated the problem was more low-level within the Linux distro. An engineer from Nvidia wasn't able to find the cause. If correct, this would not be the first driver bug I have found and had confirmed, by the Nvidia, AMD, and Intel driver teams.
    To make things even more difficult, the error only occurred in the release build. Debug builds could not be debugged because no error would occur!
    It never even occurred to me that the actual bone matrix data could be inputted wrong until Leadwerks user Roland reported that the bug was occurring in his game. This was the first time anyone had reported the error was occurring anywhere but the model editor.
    I finally determined that the actual bone matrices being sent to the animation shader contained many values of "-nan", meaning the negative form of "not a number". I was shocked. How could this possibly be when our animation commands have been completely reliable for years?
    I started printing values out and finally traced the problem back to the Quaternion spherical linear interpolation, or Slerp function. Slerp is a function that smoothly interpolates between two quaternion rotations without the problem of gimbal lock. This is the code for the function:
    void Quat::Slerp(const Quat& q, float a, Quat& result) { bool f = false; float b = 1.0f - a; float d = x*q.x + y*q.y + z*q.z + w*q.w; if (d<0.0f) { d = -d; f = true; } if (d<1.0f) { float om = Math::ACos(d); float si = Math::Sin(om); a = Math::Sin(a*om) / si; b = Math::Sin(b*om) / si; } if (f == true) a *= -1.0f; result.x = x*b + q.x*a; result.y = y*b + q.y*a; result.z = z*b + q.z*a; result.w = w*b + q.w*a; } In the function above, "a" is an interpolation value. I found that when a was equal to 0.0 the function would sometimes return the -nan values, but only when compiled in release mode with the GCC compiler! Adding a quick check at the beginning of the function fixed the problem: if (a==0.0f) { result.x = x; result.y = y; result.z = z; result.w = w; return; } And with that, it appears this issue can finally be put to rest. I think the lesson I learned here is always go where the bug leads to, even if you are sure there isn't a problem there.
  3. Josh
    I recently published the full source code to my little mini-game "Asteroids3D". You can download it here:
    Turning this into a VR game with Leadwerks Engine 4.5 was very easy.  I will show you how here.
    The first step is to enable VR.  This code will check to see if OpenVR initializes correctly. If it fails for any reason, an error message will be printed and the game will exit:
    if VR:Enable()==false then System:Print("VR failed to initialize.") return end Now we need to adjust some settings.  If you are converting an existing game into a VR game, you will probably use seated tracking space. This allows the player to sit and use the keyboard and mouse just like they would in the non-VR game:
    VR:SetTrackingSpace(VR.Seated) With seated VR mode the camera offset (your body's position and rotation) is automatically added to the camera when rendering, so there's nothing else to worry about.
    After the map loads, right before your main loop starts, you should add a command to recenter the headset tracking.  I added a five second delay beforehand to make sure the user has time to adjust their headset:
    Time:Delay(5000) VR:CenterTracking() If you want to mirror the display to the window, you can call VR:MirrorDisplay() right before Context:Sync().  (If you don't want this feature you can actually just remove the Sync() call completely):
    VR:MirrorDisplay(context) context:Sync(false) Sync will always disable VSync on the window when VR mode is enabled, since the headset refreshes at a higher rate than your monitor, and we don't want anything slowing it down.
    The rest of your game will work just as it would normally. That's really all there is to creating a seated VR game! You can download the VR project here.
     
  4. Josh
    Another build is available on the beta branch on Steam.
    Fixed zip import behavior. Fixed physics swept collision bug, waiting for confirmation this fixed other reported problems. Kinematic joint rotation not fixed yet. Updated to Steamworks 1.41 Updated to latest version of OpenVR Code::Blocks C++ project not updated yet. We're getting close to release, so your reports (especially the ones with examples!) are appreciated.
    Instructions for updating C++ projects are available here:
    https://www.leadwerks.com/community/topic/16838-upgrading-44-c-projects-to-45/
     
  5. Josh
    The beta branch on Steam is updated with some fairly big changes.
    Leadwerks Engine is now compiled with and uses Visual Studio 2017. It is now compiled on Ubuntu 16.04.  I do not know yet what changes this means for the end user but I had to add some dependencies in the editor. In addition to that, ZIP import is now working (File > Import).
    The VR command set is finished and documented.  A VR project template is forthcoming.
    The new vehicles are not yet ready.
    Leadwerks Game Engine 4.5 will be released December 22nd.
     
  6. Josh
    Leadwerks Engine 4.5 will add ZIP files to the list of file formats you can import into the editor.  This isn't really anything special except that it works in a very specific way.  Source art files will be extracted first, followed by final game-ready formats (mdl and tex), followed by .meta files, which contain thumbnails and conversion settings for each file.  The resulting behavior is that you can import a zip file of game assets, with source art files, and the files will be copied in a way so that no reconversion of the files is triggered, because the time stamps are sequenced in the correct order.
    It's the little things. 
  7. Josh
    The beta branch has been updated with a new build of version 4.5 beta.  VR support is implemented and documented here:
    https://www.leadwerks.com/learn?page=API-Reference_Object_VR
    Linux C++ projects will currently not build in the beta (working on it now).
    See this thread on updating your C++ projects:
    https://www.leadwerks.com/community/topic/16838-upgrading-44-c-projects-to-45/
     
  8. Josh
    Leadwerks has historically had a small group of customers outside of the game industry who use our software for simulations, training, and visualization.  Customers using our software products include NASA, Lockheed Martin, Northrop Grumman, and the British Royal Navy.  Today I am happy to announce that in response to overwhelming demand we are now offering our services to build custom VR applications with Leadwerks.
     
     
    This puts us in head-to-head competition with other services firms who are mostly using the Unity3D engine to put out quick results.  However, longstanding design decisions going back years have put Leadwerks Software in a position that gives us very strong advantages in the VR market.  In this article I will explain how we are leveraging our unique competitive advantages to provide the most compelling results for your VR project.
    Leadwerks vs. Unity3D for Virtual Reality
    Most of our competitors have tried to take shortcuts by building on a platform with severe limitations, using the Unity 3D engine together with the C# programming language. This 3D engine is primarily used for mobile games, and the C# programming language was originally created for event-driven business applications.
    We on the other hand have built our own 3D development system that is specifically designed to capture the maximum capabilities of VR.  Our 3D engine is built specifically for high-end PCs, with graphical fidelity and performance as our overarching principles. We use the C++ programming language which is the standard for any computationally intensive code, including operating systems, device drivers, high-frequency trading software, and virtual reality applications, which must operate at a steady 90 frames per second to prevent nausea. Our development approach brings several significant competitive advantages we can now offer to you.
    C/C++ Interoperability
    Virtually all major scientific and engineering libraries like MATLAB, etc. are written in C or C++.  Because our VR development platform is written in pure C++ we can seamlessly integrate with all of your existing C and C++ code. For example, actual satellite control code could be compiled into a simulation and run seamlessly to test how the spacecraft would react to a variety of simulated conditions. All scientific and engineering code libraries are easily accessible from a Leadwerks project.
     
    Competitors using C# and the Unity 3D engine will encounter roadblocks when they attempt to interface with C/C++ code. An intermediate wrapper has to be written that converts object-oriented code into procedural commands. This process is time-intensive and prone to breakage when APIs change with new versions. Integration of C/C++ code with Leadwerks, on the other hand, is instantaneous and seamless.
    Performance
    Nausea is a serious consideration in VR. If a discrepancy exists between the inputs received by the operator’s ocular and vestibular systems, it will result in motion sickness. An engineering tool designed to be used for long periods of time must maintain a steady 90 frames per second, allowing only 11 milliseconds for each frame render. Unfortunately, C# is a memory-managed language meaning it suffers overall slower performance, as well as periodic pauses in program execution while garbage collection is performed. All of our code is written in C++ and will perform at the maximum speed allowed by the hardware. This allows us to create richer VR applications with expanded capabilities while our competitors will run into performance problems that cause unpleasant physiological symptoms.
     

    Benchmark showing execution time of C++ vs. C#.
    Source: https://www.codeproject.com/Articles/212856/Head-to-head-benchmark-Csharp-vs-NET
    Source Code Modification
    Because we developed our own 3D engine we have full access to the entire source code and can make modifications to expand its capabilities (5). For example, we learned that some aerospace clients were experiencing problems with 32-floating point precision in some applications, so we re-compiled our software using 64-bit floating points, raising the maximum area we can simulate up to one cubic light year with sub-millimeter precision. Because our competitors do not have source code access to the 3D engine they are using, their ability to elastically scale their capabilities and customize their 3D engine for your needs will be greatly impeded.
    Accuracy of Simulated Physics
    Our software features a fast and stable Newtonian physics system that provides the most realistic physics simulation possible at real-time speeds. As the video above demonstrates, this can be used to simulate robotic arms and other moving mechanical features with a high degree of realism.
     
    The Unity physics system was designed for games and runs on the graphical processing unit (GPU). GPUs are good at performing massive parallel processing computations but are not good at problems that involve a lot of data exchange. Unfortunately, colliding objects are a problem that involves a high degree of data exchange between threads, so the accuracy of the simulation is compromised. This has two significant consequences. First, physics in Unity tend to be much less stable than in Leadwerks, making it difficult to simulate complex jointed systems like a robotic arm. A video showing the difference can be seen here.
     

    Rigid body stacking test: Leadwerks physics (green) are stable while Unity physics (yellow) spontaneously collapse.
    Second, physics in Unity are non-deterministic. This means that each time a simulation is run, the result will be different, making it very difficult to predict outcomes. The Leadwerks physics system is deterministic and will provide the exact same result each time it is run, even if new objects are introduced into the simulation.
    The competitive advantages we can put to work for your VR project are summarized below.  Simply put, we can build applications that are bigger, faster, and have more capabilities.
     
      Other firms using Unity
    Leadwerks VR Services
    Primary platform of 3D engine
    Mobile phones
    High-end PCs
    C/C++ Interoperability
    Requires C# wrapper
    Seamless
    Performance
    Slower with GC pauses, results in nausea
    Fastest possible performance
    3D engine source code modification
    No
    Yes
    Physics simulation
    Unstable, non-deterministic
    Stable, deterministic
    Maximum range with sub-mm precision
    Eight kilometers
    One light year
    If you are interested in taking advantage of our capabilities to build VR applications send us an email, or catch me at I/ITSEC later this week.
  9. Josh
    The revision of our vehicle system has been a long time coming, and I am happy to say the wait will soon be worthwhile.  The new system uses a hyperrealistic slip differencial system to adjust the power given to each wheel.

    The results are a clear cut above any game vehicle physics you've ever seen.
    I am also taking steps to make the vehicle system easier to use.  The AddTire() command can now accept an entity as a parameter for the tire object.  This would typically be a child limb found within a loaded car model.  The object's size can be used to calculate the exact width and radius for the tire.  This simplifies the vehicle setup code so you just load up the model, find the wheel limbs, add tires and axles, and build the vehicle:
    //Load car model auto chassis = Model::Load("Models/Desert Patrol/Desert Patrol.mdl"); chassis->SetMass(2000); vehicle = Vehicle::Create(chassis); //Add tires vehicle->AddTire(chassis->FindChild("WheelFL"), true); vehicle->AddTire(chassis->FindChild("wheelFR"), true); vehicle->AddTire(chassis->FindChild("WheelBL14")); vehicle->AddTire(chassis->FindChild("WheelBL15")); //Add axles vehicle->AddAxle(0, 1); vehicle->AddAxle(2, 3); //Finalize the vehicle vehicle->Build(); It's very easy to load up a model and turn it into a working vehicle without worrying about the exact tire sizes and positions.

    You no longer have to position the visible tire models yourself to match the tire orientations, as this is all done automatically.  So we've taken a very complicated advanced system and made it very accessible and quick to get running.
  10. Josh
    In this article I am going to explain some simple color tricks you can use to give your game environments a much more cohesive color scheme and look much better.
    Now if you look at most indie game environments that are thrown together from random models they will look something like this:

    It looks awful, with no consistent color scheme and saturation levels all over the place.  In fact, most look much worse than this.  I'm going to show you how to fix this.
    Control Saturation Levels
    The first step is to limit your saturation levels.  You can see the rocks and plants have fairly muted colors but the building, train engine, and cargo containers stick out like sore thumbs.  They sky also looks like a smurf.  You want to limit your saturation levels so that you can later apply lighting to the scene and get a more uniform look.  To do this you need to open the source images of your textures in Photoshop or another paint program and run a desaturation filter on any that look too colorful.  The image below shows a more desaturated scene, which still doesn't look great but it's a step in the right direction.

    Use a Good Skybox
    In an outdoor scene the sky typically takes up about 50% of your screen pixels, so it's important for this to look good.  There are only two types of good skyboxes.  You can either used one based on real photos of the sky, or use one created in the Vue Esprit software program.  Anything else is bad.  (Please do not use Terragen to make your skies!)  Photo-based skyboxes have the advantage of almost always looking great, while procedural skies created in Vue give you a lot more control over exactly how the sky should look.  (I used Vue for the default skybox that comes with Leadwerks.)  The scene below shows the photo-based skybox we used for The Zone DLC:

    Colored Lighting
    In nature, light is almost never white, but there's not really any such thing as "colored light", there's just light.  Light has properties of both particles and waves, and can travel at different wavelengths.  You have three types of photoreceptors in your eyes that each detect a different range of wavelengths.  These different receptors perceive light as red, green, and blue, but it's really all just light.  The combination of these different receptors inputs gives us all the colors in-between like turquoise (blue + green), yellow (red + green), and purple (red + blue).

    Blue light has the shortest wavelength and is the most powerful, while red has the longest wavelengths and is weakest.  Consequently, when light is dim it actually appears tinted blue because we can see the blue wavelengths more strongly.  This is easily visible at night when everything you see has a subtle blue tint.

    In the screenshot below I changed the ambient light level from RGB 64,64,64 to RGB 61,61,86.  It's not an extreme change but you can see the rocks look a little more natural.

    The ambient light level is added to the entire scene on top of any other lighting.  If our sunlight is pure white then our entire scene will have a blue tint when we add in the blue ambience.  Just to counteract this we need to tint the sunlight a little bit with a color that is low in blue light.  RGB 255,255,0 is bright yellow, and guess what color sunlight tends to be?  It usually range anywhere from yellow to pink depending on the time of day and atmospheric conditions.

    *The high school I attended Is actually named after this mountain in California.

    Since our skybox already has a nice orange glow in some places, I decided to use that for our sun color.

    Because my saturation levels are kept in check the whole scene now has a nice orange tint to it, while retaining the original reds, yellows, and greens of the environment.  The orange counteracts the ambient blue in areas that have direct sunlight, while a subtle blue tint is retained on shaded surfaces.
    Post-Process Effects
    My basic stack of post-processing effects I use are SSAO and standard bloom.  I also add the iris adjustment effect for outdoor scenes, as this allows us to simulate a greater range of lighting intensities.  Here is the final scene with these post-process effects added.

    Notice the whole scene has an overall theme of blue, orange, and very subtle green/brown.  The bloom filter tends to increase perceived color saturation a bit, so the blue shadows are clearly visible now when contrasted to the orange glow.  Compare this to our image at the beginning of this article.  Which looks better to you?
    This is the process I use for pretty much any game level I make, and it will make your games look 200% better.
  11. Josh
    You've learned Leadwerks, built a basic game, and published it to the Game Launcher, perhaps during one of the many Leadwerks game tournaments. Now what?
    Fortunately, there is a big appetite for work-in-progress indie games. The next step is to create an account on itch.io and GameJolt and upload your standalone game. Put your game out there on these platforms and if it is fun it will gain a lot of players.
    At the same time, there's a ton of talented YouTubers out there making reviews of indie games. Contact them with a link to your itch.io or GameJolt page and they will produce a funny and entertaining video you can use to promote your game. Look at all these creative videos that just two games have generated. (There's a lot more than this, but I got tired of cutting and pasting URLs!)
     
     
     
     
     
     
     
     
     
    http://www.youtube.com/watch?v=Uv-2WNuzqrM&t=94s
    http://www.youtube.com/watch?v=xy4SvY6kg9o
    http://www.youtube.com/watch?v=NgG6mlmgT5A
    http://youtu.be/UoXG9jrT77Y
    http://www.youtube.com/watch?v=oIdHsTpl60U
    http://www.youtube.com/watch?v=kcP-pOe0znM
    http://www.youtube.com/watch?v=sSr5n3N7O_M
    http://youtu.be/x6ht8JbWkwg
    http://www.youtube.com/watch?v=czJ_lHeztu4
    You should gather feedback from your users in the comments sections of itch.io and GameJolt. You can also use the built-in analytics features in Leadwerks with a GameAnalytics.com account to get a view of how your users are playing your game. If only 5% of users can make it to level two, you might want to rethink the difficulty of level one!
    Finally, when you feel you are ready, you should launch a Greenlight campaign. Because you prepared by building a community, promoting your game with videos, and getting feedback from users, you will be in a much better position than someone who just appears out of nowhere.
  12. Josh
    Version 4.5 will feature official support for VR (Vive and Oculus) and a new super realistic vehicle system.  These features are more or less already done, but need some work to make them release-ready.  The new vehicle system is so good, and I am so grateful to Julio for his help making this happen, that I think we might do a racing-themed winter tournament.   I am trying to think of a clever pun to call it.
    At the same time, I have implemented the first build of version 5 in the same source code, adding Unicode support and using C++11 shared pointers for all objects that were previously created as pointers.  The changes to the source code were extensive, and though I used preprocessor definitions to separate the 4.x and 5 builds, there may be some errors in the engine at this time.
    So the next few weeks is going to be a process of testing the engine and documenting the new features.  I plan to have a beta of 4.5 up shortly on Steam.  4.5 will be released before Christmas, along with the first beta of version 5, for people who bought the $5 subscription.  The enterprise version will be updated a couple weeks after the release of 4.5.
  13. Josh
    Our website stores a lot of user generated content in the forum of images and attachments.  Before Leadwerks Game Engine was on Steam the demands were even higher, since we had our own downloads and gallery sections that stored data on our server.  Since the implementation of Steam screenshots and Workshop a lot of that has been offloaded onto the Steam servers, relieving our server from some of the data storage and transfer costs.  (If you're interested, all our old content is archived on Google drive here.)
    Currently our website weighs in at about 35 GB of data.  This is backed up online daily, and offline about once a month.  The entire server is usually burned onto a Blu-Ray disc and saved away.  The time and storage space this takes is considerable, and as the site keeps growing this approach will not be sustainable.

    Amazon S3 allows you to store files in the cloud with an API to write and save files, at a cost of $0.023 (that's 2.3 cents) per GB per month.  I've hooked into the service to offload all user attachments, images, and profile pictures onto Amazon's servers.  This leaves our core site data at an easily manageable 2.5 GB, which I can easily burn onto a DVD.
    Now that all user data is stored in the AWS system for dirt cheap prices, we can easily grow the amount of content on our site without it impacting the site responsiveness, backup time, or having much impact on operating costs.  Stay tuned and I will tell you how we are going to use this for game development.
     
  14. Josh
    Our most recent game tournament was a smashing success.  We had fewer entries this time, but they more than made up for it with the great quality of this round of games.  Without further ado I am happy to present the entries...
    Behind Enemy Lines
    Wow!  This game by burgelkat features a variety of missions from blowing up drug manufacturing facilities to sabatoging a plane.  Although the same mechanic is usually used, the action never gets old and you will keep playing just to find out what will happen next.  You may recognize the voice acting from our own Jorn Theunissen (Aggror) on the forum.  You don't want to miss this one!



    Nightmare Prism
    Nirvana is popular right now, and the SNES classic edition was just released.  In case that's not enough 1990's nostalgia for you, here is the excellent Nightmare Prism by AngelWolf.  Clever level design with lots of traps and well-placed enemies will keep you on your toes as you frag your way through three levels of hellish onslaughts.
    The Cemetery
    Third on our list of games is an explicitly Halloween-themed title with tombstones and pumpkins aplenty.  The Cemetery by Rozsoft is a short but suspenseful experience putting you into an old graveyard at night in search of your disappeared friends.  Best played late at night with the lights out!


    Dread Loop
    This title by member "FortifyThisMFker!" brings us back to the 90's shooter theme with a center-mounted gun and an arena of enemies.  After dispatching your foes you can select an upgrade for your weapon, health, or suit, which makes for some interesting choices.  But I've got to be honest, seeing the giblets fly is what really makes this game fun.  Try it out!


    Exit Zed
    In Exit Zed by mdgunn you will explore a scientific facility in search of zombies to high-five...except that the way you like to give high-fives is with your handy dandy fully automatic Bunsen burner tool (patent pending).  The game is obviously unfinished and you might stumble across some doorways leading to nothingness, but the sound effect of your trusty scientific tool alone makes this worth playing.


    Dissension
    Available in Leadwerks Game Launcher, Dissension is another nail-biting SciFi shooter from Garlic Waffle.  Despite the cartoonish graphics, his games really frightening.  This one is sure to keep you on the edge of your seat!


    Sewer Survival
    Garlic Waffle has gone into overtime and brought your TWO free games to play this tournament!  In Sewer Survival you play to make it out of an underground prison.  Expect clever puzzles, loads of enemies, and not a lot of hit points.


  15. Josh
    Today we are pleased to announce the release of Leadwerks Game Engine: Enterprise Edition, a standalone version of our popular 3D development software. The Enterprise Edition allows business users to install and use Leadwerks without the need for the Steam client. The new product joins the existing Standard Edition with Lua scripting and the Professional Edition with C++ and Visual Studio support, both sold on Steam.
    The Enterprise Edition has already been approved for sale through NASA’s ACES approval program for software, and NASA has joined a lineup of business customers using Leadwerks products that includes Lockheed Martin, Northrop Grumman, and the British Royal Navy.
    In the near future the capabilities of our software will continue to expand to support the needs of business customers in the VR and simulation spaces. The development of Leadwerks Game Engine 5 is being directed with a focus on performance, hyperrealism, and improved ease of use.
    Leadwerks Game Engine: Enterprise Edition is available to purchase for $499 on our website.

    Image courtesy of NASA Satellite Servicing Projects Division.
  16. Josh
    Just thinking out loud here, and I am considering designing the GUI like this:
     
    The GUI class handles events, window clipping, widget layout, etc. Rather than having a bunch of C++ classes that extend a base Widget class, there would just be the Widget class with a Lua script attached to it. The Lua script would handle all events and drawing, so it would determine what kind of widget the C++ object is. For example, here's a script for a simple button:
     

    function Script:Draw(x,y,width,height) if self.pointerhovered==true then self.widget.window:SetColor(self.background.r*1.2,self.background.g*1.2,self.background.b*1.2) else self.widget.window:SetColor(self.background.r,self.background.g,self.background.b) end self.widget.window:DrawRect(self.x,self.y,self.width,self.height) self.widget.window:SetColor(self.background.r,self.background.g,self.background.b) self.widget.window:DrawText(self.text,self.x,self.y,self.width,self.height,Text.Center+Text.VCenter) end function Script:MouseEnter(x,y) self.pointerhovered=true self:Draw(self.x,self.y,self.width,self.height) end function Script:MouseLeave(x,y) self.pointerhovered=false self:Draw(self.x,self.y,self.width,self.height) end function Script:MouseUp(x,y) if self.pointerhovered==true then self.widget.gui:EmitEvent(Widget.ActionEvent) end self.buttonpressed=false end function Script:MouseDown(x,y) if self.pointerhovered==true then self.buttonpressed=true end end
     
    The upside:
    It's infinitely extensible and can be used to make literally any kind of widget.

     
    The downside:
    The script needs to contains the logic and the drawing, so although skinning is possible, it's not as simple as choosing a couple of colors.
    Complexity in the interaction between C++ and Lua widget logic.
    Debugging editor scripts as they are running = not fun. If people modify the editor GUI scripts it could introduce crashes.
    Coding advanced widgets like a treeview will be quite complex in Lua.

     
    Anyways, I'm just experimenting with it for now.
     

  17. Josh
    Fall is in the air.  The leaves are changing colors, people are bundling up, and game developers are itching to try their hand at another community game tournament.  How does it work?  For 30 days, the Leadwerks community builds small playable games.  Some people work alone and some team up with others.  At the end of the month, on Halloween day, we release our projects to the public and play each other's games.  The point is to release something short and sweet with a constrained timeline, which has resulted in many odd and wonderful mini games for the community to play.
    WHEN: The tournament begins Sunday, October 1, and ends Tuesday, October 31st at the stroke of midnight.
    HOW TO PARTICIPATE: Publish your Halloween-or-other-themed game to Steam Workshop or upload it to itch.io before the deadline. You can work as a team or individually. Use blogs to share your work and get feedback as you build your game. If you need models for your game, we've got a Halloween Model Pack for you to use for free from Leadwerks Workshop.
    Games must have a preview image, title, and contain some minimal amount of gameplay (there has to be some way to win the game) to be considered entries. It is expected that most entries will be simple, given the time constraints.
    On November 1st we will post a roundup blog featuring your entries.
  18. Josh
    Today I am excited to announce plans for the release of the first Leadwerks 5 beta version.  Leadwerks 5 will roll out sooner rather than later, employing an extended beta period during which versions 4 and 5 will live side-by-side, using the same code base, with preprocessor definitions to compile each version.  This allows me to fix small problems without forking the code, while I can implement new changes in version 5.  The first features implemented will be the use of smart pointers for all shared objects, and unicode support for all strings.
    A subscription model will be available for access to the Leadwerks 5 beta, at a modest price of just $4.99/month for enthusiasts who want access to the most cutting-edge game development technology as it is developed.  This will be available through the Leadwerks.com site, and will not use Steam (at least at first).  I feel it is important for the company's future to start building a recurring revenue stream, and I want to create something that does not rely on any middleman who may arbitrarily change or discontinue the terms of the service they are providing.  The Leadwerks 5 beta will implement breaking changes as it is developed, and is not meant for use in a production environment, so I do not recommend moving any commercial projects from version 4 to 5.  Leadwerks 4.x will continue to receive updates and new features until the final version 5 is released.
    Leadwerks 5 is designed to be the most advanced game engine in the world, combining improved ease of use with massive performance, and a special emphasis on VR.  Thank you for supporting the next generation of game development technology.
  19. Josh
    I've begun implementing unicode in Leadwerks Game Engine 5.  It's not quite as simple as "switch all string variables to another data type".
    First, I will give you a simple explanation of what unicode is.  I am not an expert so feel free to make any corrections in the comments below.
    When computers first started drawing text we used a single byte for each character.  One byte can describe 256 different values and the English language only has 26 letters, 10 numbers, and a few other characters for punctuation so all was well.  No one cared or thought about supporting other languages like German with funny umlauts or the thousands of characters in the Chinese language.
    Then some people who were too smart for their own good invented a really complicated system called unicode.  Unicode allows characters beyond the 256 character limit of a byte because it can use more than one byte per character.  But unicode doesn't really store a letter, because that would be too easy.  Instead it stores a "code point" which is an abstract concept.  Unfortunately the people who originally invented unicode were locked away in a mental asylum where they remain to this day, so no one in the real world actually understands what a code point is.
    There are several kinds of unicode but the one favored by nerds who don't write software is UTF-8.  UTF-8 uses just one byte per character, unless it uses two, or sometimes four.  Because each character can be a different length there is no way to quickly get a single letter of a string.  It would be like trying to get a single byte of a compressed zip file; you have to decompress the entire file to read a byte at a certain position.  This means that commands like Replace(), Mid(), Upper(), and basically any other string manipulation commands simply will not work with UTF-8 strings.
    Nonetheless, some people still promote UTF-8 religiously because they think it sounds cool and they don't actually write software.  There is even a UTF-8 Everywhere Manifesto.  You know who else had a manifesto?  This guy, that's who:

    Typical UTF-8 proponent.
    Here's another person with a "manifesto":

    The Unabomber (unibomber? Coincidence???)
    The fact is that anyone who writes a manifesto is evil, therefore UTF-8 proponents are evil and should probably be imprisoned for crimes against humanity.  Microsoft sensibly solved this problem by using something called a "wide string" for all the windows internals.  A C++ wide string (std::wstring) is a string made up of wchar_t values instead of char values.  (The std::string data type is sometimes called a "narrow string").  In C++ you can set the value of a wide string by placing a letter "L" (for long?) in front of the string:
    std::wstring s = L"Hello, how are you today?"; The C++11 specification defines a wchar_t value as being composed of two bytes, so these strings work the same across different operating systems.  A wide string cannot display a character with an index greater than 65535, but no one uses those characters so it doesn't matter.  Wide strings are basically a different kind of unicode called UTF-16 and these will actually work with string manipulation commands (yes there are exceptions if you are trying to display ancient Vietnamese characters from the 6th century but no one cares about that).
    For more detail you can read this article about the technical details and history of unicode (thanks @Einlander).
    First Pass
    At first I thought "no problem, I will just turn all string variables into wstrings and be done with it".  However, after a couple of days it became clear that this would be problematic.  Leadwerks interfaces with a lot of third-party libraries like Steamworks and Lua that make heavy use of strings.  Typically these libraries will accept a chr* value for the input, which we know might be UTF-8 or it might not (another reason UTF-8 is evil).  The engine ended up with a TON of string conversions that I might be doing for no reason.  I got the compiler down to 2991 errors before I started questioning whether this was really needed.
    Exactly what do we need unicode strings for?  There are three big uses:
    Read and save files. Display text in different languages. Print text to the console and log. Reading files is mostly an automatic process because the user typically uses relative file paths.  As long as the engine internally uses a wide string to load files the user can happily use regular old narrow strings without a care in the world (and most people probably will).
    Drawing text to the screen or on a GUI widget is very important for supporting different languages, but that is only one use.  Is it really necessary to convert every variable in the engine to a wide string just to support this one feature?
    Printing strings is even simpler.  Can't we just add an overload to print a wide string when one is needed?
    I originally wanted to avoid mixing wide and narrow strings, but even with unicode support most users are probably not even going to need to worry about using wide strings at all.  Even if they have language files for different translations of their game, they are still likely to just load some strings automatically without writing much code.  I may even add a feature that does this automatically for displayed text.  So with that in mind, I decided to roll everything back and convert only the parts of the engine that would actually benefit from unicode and wide strings.
    Second Try + Global Functions
    To make the API simpler Leadwerks 5 will make use of some global functions instead of trying to turn everything into a class.  Below are the string global functions I have written:
    std::string String(const std::wstring& s); std::string Right(const std::string& s, const int length); std::string Left(const std::string& s, const int length); std::string Replace(const std::string& s, const std::string& from, const std::string& to); int Find(const std::string& s, const std::string& token); std::vector<std::string> Split(const std::string& s, const std::string& sep); std::string Lower(const std::string& s); std::string Upper(const std::string& s); There are equivalent functions that work with wide strings.
    std::wstring StringW(const std::string& s); std::wstring Right(const std::wstring& s, const int length); std::wstring Left(const std::wstring& s, const int length); std::wstring Replace(const std::wstring& s, const std::wstring& from, const std::wstring& to); int Find(const std::string& s, const std::wstring& token); std::vector<std::wstring> Split(const std::wstring& s, const std::wstring& sep); std::wstring Lower(const std::wstring& s); std::wstring Upper(const std::wstring& s); The System::Print() command has become a global Print() command with a couple of overloads for both narrow and wide strings:
    void Print(const std::string& s); void Print(const std::wstring& s); The file system commands are now global functions as well.  File system commands can accept a wide or narrow string, but any functions that return a path will always return a wide string:
    std::wstring SpecialDir(const std::string); std::wstring CurrentDir(); bool ChangeDir(const std::string& path); bool ChangeDir(const std::wstring& path); std::wstring RealPath(const std::string& path); std::wstring RealPath(const std::wstring& path); This means if you call ReadFile("info.txt") with a narrow string the file will still be loaded even if it is located somewhere like "C:/Users/约书亚/Documents" and it will work just fine.  This is ideal since Lua 5.3 doesn't support wide strings, so your game will still run on computers around the world as long as you just use local paths like this:
    LoadModel("Models/car.mdl"); Or you can specify the full path with a wide string:
    LoadModel(CurrentDir() + L"Models/car.mdl"); The window creation and text drawing functions will also get an overload that accepts wide strings.  Here's a window created with a Chinese title:

    So in conclusion, unicode will be used in Leadwerks and will work for the most part without you needing to know or do anything different, allowing games you develop (and Leadwerks itself) to work correctly on computers all across the world.
  20. Josh
    I have implemented C++11 shared pointers into Leadwerks Game Engine 5 and the following program now works.  When you press the space key the box variable is set to NULL and the visible box on the screen disappears:
    #include "Leadwerks.h" using namespace Leadwerks; int main(int argc, const char *argv[]) { auto window = CreateWindow(); auto context = CreateContext(window); auto world = CreateWorld(); auto camera = CreateCamera(world); camera->Move(0,0,-5); camera->SetClearColor(0,0,1); auto light = CreateDirectionalLight(world); auto box = CreateBoxModel(world); while (not window->Closed()) { if (window->KeyHit(SpaceKey)) box = NULL; if (box) box->Turn(0, 1, 0); world->Update(); world->Render(context); } return 0; } Shared pointers provide the ease of use of a memory-managed language like C# but run much faster because they aren't using full garbage collection.  The one thing you have to watch out for is circular references.  In the example below, neither a or b would ever be deleted from memory, since they reference each other:
    class thing { shared_ptr<thing> friend; }; auto a = make_shared<thing>(); auto b = make_shared<thing>(); a.friend = b; b.friend = a; a = NULL; b = NULL; This problem can be solved by using weak pointers:
    class thing { weak_ptr<thing> friend; shared_ptr<thing> GetFriend(); }; shared_ptr<thing> thing::GetFriend() { return friend.lock(); } auto a = make_shared<thing>(); auto b = make_shared<thing>(); a.friend = b; b.friend = a; a = NULL; b = NULL; When using shared pointers you need to think about hierarchical relationships.  For example, a parent entity stores a list of shared pointers for its child entities, but each child uses a weak pointer to store its parent.  This makes it so children will not be deleted as long as you hold onto the parent entity:
    class Entity { std::list<shared_ptr<Entity> > kids; weak_ptr<Entity> parent; }; These relationships are not always immediately obvious.  For example you might think that a world should control when entities are in scope, but that would not allow our first example above.  Instead, entity creation functions return a shared_ptr and the world octree only stores weak_ptr values to entities.  When you want to delete an entity, just set it to NULL.  If you have large collections of entities you can just store them in a list and clear the list when you are ready.  There is no World::Clear() function since the entities are managed by your own code.  I'm not 100% sure on how map loading will work with this, but so far it is working out well.
    One thing to watch out for is that Self() or shared_from_this() cannot be accessed in the object constructor or destructor.  This will require some changes to how Leadwerks handles destruction of some objects, since previous versions uses manual deletion and removed lots of list iterators from different parts of the game world.  You might run into situations where you have a lot of weak pointers stored in lists like this:
    for (std::list<weak_ptr<Entity> > it = list.begin(); it!=list.end(); it++) { auto entity = (*it).lock(); //do some stuff } Here is how you can modify the loop to handle the situation where an expired weak pointer is encountered (the object was deleted):
    std::list<weak_ptr<Entity> > it = list.begin(); while (it != list.end()) { auto entity = (*it).lock(); if (entity == NULL) { list.erase(it++); continue; } //do some stuff it++; } This will skip deleted objects and trim the list so that it doesn't grow out of control.
    The new paradigm of C++11 shared pointers requires a little bit different approach but is much easier to use and does a good job of preventing mistakes especially when your game gets more complex.
  21. Josh
    All classes in Leadwerks are derived from a base Object class.  In Leadwerks 5 we separate simple and complex objects with the new SharedObject class.
    Simple objects like a Vec3 ( a three-dimensional vector), an AABB (axis-aligned bounding box), and other items are all derived from the Object class.  Simple objects are created with constructors.  When we make one object equal to another the value is copied from one variable to another, but the two variables are still separate objects.  Below, A and B have the same value but are separate objects:
    Vec3 a = Vec3(1,2,3); Vec3 b = a; Shared objects are things you don't want to copy, because they involve more than just some numbers.  These always use C++11 shared pointers and use a Create function.  Below, A and B refer to the same object:
    shared_ptr<World> a = CreateWorld(); shared_ptr<World> b = a; The SharedObject class has a couple of functions to make life easier.  Instead of casting pointers with some funny syntax we can use the Cast() method.  Here's an example of casting in Leadwerks 4:
    Entity* entity = Model::Load("car.mdl"); Model* model = (Model*)entity; And here's how it works in Leadwerks 5:
    shared_ptr<Entity> entity = LoadModel("car.mdl"); shared_ptr<Model> model = entity->Cast<Model>(); Instead of using "this" inside a class method you can use Self() to get a shared pointer to the object itself:
    class MyActor : public Actor { void MyFunction() { //MyActor* me = this; shared_ptr<SharedObject> me = Self(); } } Self() will always return a shared_ptr<SharedObject> value, so you can use Cast() if you need a specific type of object (and match the behavior of "this"):
    class MyActor : public Actor { void MyFunction() { //MyActor* me = this; shared_ptr<MyActor> me = Cast<MyActor>(); } } Instead of calling delete or Release to destroy a shared object, all you have to do is set its variable to NULL:
    shared_ptr<Model> model = LoadModel("car.mdl"); model = NULL;// poof! And of course we can always use the auto keyword to make things really simple:
    auto model = LoadModel("car.mdl"); Shared objects use automatic reference counting to give you the ease of use of a garbage-collected language, together with the blazing performance of modern C++.  These features are set to make Leadwerks Game Engine 5 the easiest and most cutting-edge development system in the history of game programming.
  22. Josh
    Leadwerks Game Engine 5 moves Leadwerks forward into the future with massive performance increases and more advanced features, it also makes game development easier than ever before with three great new programming features.
    Shared Pointers
    I could write a whole article about the benefits of shared pointers.  Shared pointers are basically a simple form of garbage collection that relieves you from the need to manually delete objects, but doesn't suffer from the slow speed of full garbage collection.like C# uses.
    In Leadwerks 4 we need to keep track of object reference counts by using the Release command when we are done with them:
    Texture *texture = Texture::Load("Materials/Bricks/brick01.tex"); Material *material = Material::Create() material->SetTexture(texture); Model *model = Model::Box(); box->SetMaterial(material); material->Release(); if (texture) texture->Release(); In Leadwerks 5 we never have to worry about calling Release() or (less commonly used) AddRef() because reference counts of shared pointers are tracked automatically.  The code below would cause a memory leak in Leadwerks 4 but is perfectly fine to use in Leadwerks 5:
    auto material = Material::Create() material->SetTexture(Texture::Load("Materials/Bricks/brick01.tex")); auto model = Model::Box(); box->SetMaterial(material); This even works with entities.  As soon as a variable goes out of scope the object is deleted:
    if (true) { auto model = Model::Box(); }// automatic deletion occurs here
    We can prevent deletion of an object by either keeping the variable in our code, or keeping a container that holds it.  In the case of map loading, the Map:Load() will return a structure that contains all the entity handles so they stay in memory.  Want to delete a camera?  It's easy:
    camera = NULL;// poof! There are still some details to work out but so far this approach is working great.
    Another great benefit of shared pointers is that you can completely eliminate the need for big complicated destructors like this one.  All those objects will be automatically deleted when they go out of scope:
    OpenGLCamera::~OpenGLCamera() { for (int i=0; i<8; ++i) { if (shader_ambient[i]) { shader_ambient[i]->Release(); shader_ambient[i]=NULL; } for (int n = 0; n < 2; ++n) { for (int q = 0; q < 2; ++q) { for (int p = 0; p < 2; ++p) { if (shader_directional[i][n][q][p]) { shader_directional[i][n][q][p]->Release(); shader_directional[i][n][q][p] = NULL; } if (shader_directional_volume[i][n][q][p]) { shader_directional_volume[i][n][q][p]->Release(); shader_directional_volume[i][n][q][p] = NULL; } if (shader_point[i][n][q][p]) { shader_point[i][n][q][p]->Release(); shader_point[i][n][q][p] = NULL; } if (shader_point_volume[i][n][q][p]) { shader_point_volume[i][n][q][p]->Release(); shader_point_volume[i][n][q][p] = NULL; } if (shader_spot_volume[i][n][q][p]) { shader_spot_volume[i][n][q][p]->Release(); shader_spot_volume[i][n][q][p] = NULL; } if (shader_environment[i][n][q][p]) { shader_environment[i][n][q][p]->Release(); shader_environment[i][n][q][p] = NULL; } for (int usedecal = 0; usedecal < 2; usedecal++) { if (shader_spot[i][n][q][usedecal][p]) { shader_spot[i][n][q][usedecal][p]->Release(); shader_spot[i][n][q][usedecal][p] = NULL; } } } } } } } That entire function has been commented out in Leadwerks Game Engine 5.
    Finally, shared pointers eliminate a problem that is the bane of all programmers' existence.  When used correctly, you can you can say goodbye to invalid and uninitialized pointers forever!  Yet shared pointers, unlike garbage collection, are fast to use and don't slow your game down.
    Automatic Typing
    The use of C++11 in Leadwerks Game Engine 5 gives us automatic typing of variables with the auto keyword.  We can simply something like this:
    shared_ptr<Model> model = Model::Box(); To just this:
    auto model = Model::Box(); If you have long complicated type names this makes life a million times easier:
    for (std::list<shared_ptr<Entity> >::iterator it = list.begin(); it!= list.end(); it++) { shared_ptr<Entity> entity = *it; entity->SetPosition(1,2,3); } Here's the same code simplified with auto (and range-based loops thanks to @Roland and @thehankinator.  Look how much more readable it is:
    for (auto entity : list) { entity->SetPosition(1,2,3); } This isn't completely new, but Leadwerks 5 is the first version built exclusively for C++11 features.
    More Explicit API
    Leadwerks 4 uses several bound states to store a current world and context.  These are global variables that are mostly invisible to the user.  This causes problems with multithreaded programming because two threads might try to change or access the bound state at the same time.
    World *world1 = World::Create(); Model *model1 = Model::Box(); World *world2 = World::Create(); Model *model2 = Model::Box(); World::SetCurrent(world1); Model *model3 = Model::Box(); Quick, which world does "model3" belong to?  Leadwerks 5 gets rid of the concept of bound states and requires you to explicitly specify the object you want to use.  Here's how the above code would look in Leadwerks 5:
    auto world1 = World::Create(); auto model1 = Model::Box(world1); auto world2 = World::Create(); auto model2 = Model::Box(world2); auto model3 = Model::Box(world1); Not only is the second example easier to understand, it's also one line shorter.
    In a similar manner, the idea of a "current" context will be eliminated.  When you render a world you will need to explicitly specify which context to render to:
    world->Render(context) These programming features will make it easier than ever to code games with Leadwerks.
  23. Josh
    Leadwerks Game Engine 5 is being designed to make use of shared pointers.  This eliminates manual reference counting, which has probably been the most difficult part of programming games with Leadwerks.  Here are three concepts you must understand before you start using smart pointers in Leadwerks 5,
    Don't Create Multiple Shared Pointers from One Object
    When a shared pointer goes out of scope, it deletes the object it references.  If another smart pointer was created separately that references that object, the other smart pointer will now point to an object that has been deleted!  Set breakpoints in the example below and you will see the problem.  The object is deleted while the second smart pointer still references it.  Any attempt to use the second smart pointer will cause an error:
    class Thing { public: ~Thing(); }; Thing::~Thing() {}// <--------- set a breakpoint here int main(int argc, const char *argv[]) { Thing* thing = new Thing; shared_ptr<Thing> p1 = shared_ptr<Thing>(thing); shared_ptr<Thing> p2 = shared_ptr<Thing>(thing); p1 = nullptr; int k = 0;// <--------- set a breakpoint here } Instead, initialize a smart pointer once and copy it.  Here is the correct way:
    class Thing { public: ~Thing(); }; Thing::~Thing() {}// <--------- set a breakpoint here int main(int argc, const char *argv[]) { Thing* thing = new Thing; shared_ptr<Thing> p1 = shared_ptr<Thing>(thing); shared_ptr<Thing> p2 = p1; p1 = nullptr; int k = 0;// <--------- set a breakpoint here } It's even better to eliminate the new keyword entirely and create object and smart pointer in one step:
    class Thing { public: ~Thing(); }; Thing::~Thing() {}// <--------- set a breakpoint here int main(int argc, const char *argv[]) { shared_ptr<Thing> p1 = make_shared<Thing>(); shared_ptr<Thing> p2 = p1; p1 = nullptr; int k = 0;// <--------- set a breakpoint here } The point is, you create the first smart pointer and thereafter all code should pass that around.  You never need to access the pointer directly.
    Of course the use of auto makes everything a lot simpler:
    auto p1 = make_shared<Thing>(); auto p2 = p1; Parent / Child Relationships
    If you have an object that is some kind of "child" of a parent object, you probably want that parent to keep a smart pointer to the child that keeps the child from being deleted.  However, if the child has a smart pointer to the parent you are creating a circular reference that will never be deleted from memory.  Think of the parent as the owner of the child.  Something other than the child must keep the parent in memory, but sometimes the child wants to retrieve the parent object in a bit of code.  Therefore, for the child member we will use a shared pointer, and for the parent member we will use a weak pointer:
    class Thing { public: shared_ptr<Thing> child; weak_ptr<Thing> parent; ~Thing(); shared_ptr<Thing> GetParent(); }; The GetParent() function would look like this:
    shared_ptr<Thing> Thing::GetParent() { return parent.lock(); } You can modify existing functions that access a parent by adding one line of code.  Here is one such function as it would appear in Leadwerks 4, where the parent member is just a regular old stupid pointer:
    void Thing::AccessParent() { if (parent != NULL) { //do some stuff to parent here } } The updated version for Leadwerks 4 creates a new shared_ptr<Thing> variable (with auto) and locks the weak pointer to make a shared pointer.  The rest of the code works seamlessly:
    void Thing::AccessParent() { auto parent = this->parent.lock(); if (parent != NULL) { //do some stuff to parent here } } Class Functions Should Never Returns Themselves
    The following code illustrates a problematic issue:
    class Thing { public: shared_ptr<Thing> GetSelf() }; shared_ptr<Thing> Thing::GetSelf() { return shared_ptr<Thing>(this); } int main(int argc, const char *argv[]) { shared_ptr<Thing> p1 = make_shared<Thing>(); shared_ptr<Thing> p2 = p1->GetSelf(); } The GetSelf() function creates a new shared pointer that has no relation to the first one.  Both of these shared pointers will attempt to delete the object when they go out scope.  Only one will win.
    I did a search throughout the entire Leadwerks Engine project and found only three instances of the phrase "return this;".  The easiest way to fix this problem would be to eliminate this type of behavior altogether by having a parent get the object instead of the object returning itself.  For example if you have a recursive function that is structured like this:
    Thing* Thing::FindChild(const std::string& name) { if (name == this->name) return this; for (auto it = kids.begin(); it != kids.end(); it++) { auto child = (*it)->FindChild(name); if (child) return child; } return NULL; } You can restructure it like this:
    shared_ptr<Thing> Thing::FindChild(const std::string& name) { for (auto it = kids.begin(); it != kids.end(); it++) { if (name == (*it)->name) return this; auto child = (*it)->FindChild(name); if (child) return child; } return NULL; } I considered deriving all complex objects from a "SharedObject" class with a weak pointer that referenced itself:
    class SharedObject { weak_ptr<SharedObject> self; }; However, this requires the "self" member to be set when the object is created and is tedious to use.  I think it's easier to just eliminate functions that return the object itself.
  24. Josh
    This shows the fundamental difference between shared pointers and manual reference counting.
    Leadwerks 4:
    void Material::SetTexture(Texture* texture, const int index) { if (this->texture[index] != texture) { if (this->texture[index]) this->texture[index]->Release(); this->texture[index] = texture; if (this->texture[index]) this->texture[index]->AddRef(); } } Leadwerks 5:
    void Material::SetTexture(shared_ptr<Texture> texture, const int index) { this->texture[index] = texture; } The second example automatically does the same thing as the first (basically) but you don't have to worry about making mistakes.  When the texture is no longer referred to by any variable, it will be automatically deleted from system and video memory.
  25. Josh
    Previously, I talked a little bit about shared pointers in C++11.  These use automatic reference counting to track how many variables are pointing to an object.  When the object is no longer being used, it is automatically deleted.  This is similar to garbage collection, but doesn't involve the massive overhead of garbage-collected systems.  In fact, shared pointers simply automate something we were already doing with the Release() and AddRef() commands in Leadwerks 4.
    A weak pointer is like a shared pointer, but having a weak pointer to an object does not prevent the object from being deleted.  Weak pointers are a great way to handle situations that previously could have resulted in an invalid pointer.  Let's say you have an AI class for an enemy in your game that stores an entity it is attacking:
    class Monster : public Actor { int health; weak_ptr<Entity> target; virtual void UpdateWorld(); }; To make use of the weak pointer, we convert it into a shared pointer.  At that point, the temporary shared pointer will prevent the object from being deleted:
    void Monster::UpdateWorld() { shared_ptr<Entity> enemy = this->target.lock(); if (enemy != nullptr) { //do some stuff } } If something causes the monster's target entity to be deleted, the program will work just fine without having to go through and find all variables that point to that entity.
    You can check if a weak pointer's object has been deleted with the expired() command, since creating a new shared pointer involves a certain amount of overhead:
    void Monster::UpdateWorld() { if (!this->target.expired()) { shared_ptr<Entity> enemy = this->target.lock(); if (enemy != nullptr) { //do some stuff } } } However, you still need to perform the check to see if the resulting shared pointer is nullptr/NULL because theoretically another thread could have caused the object to be deleted right after the expired() call returns false.
    If you want to clean up a list of weak pointers as you discover expired pointers, you can structure it like this:
    auto it = items.begin(); while (it!=items.end()) { auto item = (*it).lock(); if (item != nullptr) { item->Update(); it++; } else { items.erase(it++); } } If we add in the optional expired() check, it gets kind of complicated, but this is not something you should really need to do much:
    auto it = items.begin(); while (it!=items.end()) { if (!(*it).expired()) { auto item = (*it).lock(); if (item != nullptr) { item->Update(); it++; } else { items.erase(it++); } } else { items.erase(it++); } } Here's a real example in the Leadwerks source code.  The LEADWERKS_5 preprocessor definition is used to modify behavior in 4/5.  In version 4 the list contains pointers, and in version 5 it contains weak pointers:  Version 4 could get complicated because the entity would have to store an iterator to remove it from this list, in case it is deleted before this routine is called inside the World::Update() function.  In version 5 we just check if the weak pointer is valid and skip it if the entity was deleted.  At the end of the routine, the whole list is cleared, so there are no worries about deleting each iterator.
    bool World::UpdateNavigation() { //Invalidate navmesh tiles that intersect moved entities for (auto it = updatenavigationlist.begin(); it != updatenavigationlist.end(); it++) { #ifdef LEADWERKS_5 auto entity = (*it).lock(); if (entity == nullptr) continue; #else auto entity = (*it); #endif entity->updatenavigationneeded = false; for (int i = 0; i<entity->relevantnavtiles.size(); i++) { entity->relevantnavtiles[i]->Invalidate(); } entity->relevantnavtiles.clear(); if (entity->navigationmode) navmesh->InvalidateTiles(entity->aabb); } updatenavigationlist.clear(); //Update the navmesh return navmesh->Update(); } Weak pointers make game programming easier and less susceptible to errors in Leadwerks Game Engine 5.
×
×
  • Create New...