Jump to content

Josh

Staff
  • Posts

    23,093
  • Joined

  • Last visited

Blog Entries posted by Josh

  1. Josh
    Here's some LE3 code for a simple program. The Graphics / Window stuff is not worked out 100%, and it's a little tricky because of the different operating systems and different kinds of windows.
     
    I think we'll see a little more consistency across various languages with the LE3 syntax. It was suggested that the C syntax use the following scheme:
    verb-class-noun

    SetEntityPosition() GetMaterialTexture()
    I agree with this suggestion.
     

    SetGraphicsDriver( OpenGLGraphicsDriver() ); CreateGraphics(1024,768,4); World* world = CreateWorld(); //Load a shader Shader* shader = LoadShader("Shaders/minimal.shader"); //Create a material; Material* mat = CreateMaterial(); mat->SetShader(shader); mat->SetColor(1,0,0,1); //Create a box; Mesh* box = CreateMeshBox(1,1,1); box->SetMaterial(mat); Camera* camera = CreateCamera(); camera->SetClearColor( Vec4(0,1,0,1) ); box->SetPosition(0,0,-2,false); float yaw = 0.0; while (!window->Closed()) { yaw++; box->SetRotation(yaw,0,0,false); camera->Render(); window->Flip(); }
  2. Josh
    Leadwerks 4.3 brings a big performance boost to your games. In this blog I am going to talk about some of the common problems you can eliminate to make your games run faster.
    When slow performance is encountered, it is typically one really bad mistake that is slowing everything down. Here's a few common bottlenecks for performance you can create in your games, how to identify them, and how to fix them.
    Shadow Updates
    Shadow rendering is cheaper than regular renders because no textures have to be used, but extra rendering passes to update shadows can add up.
    How to identify: The game stats will display the number of shadows updated. If this is more than a few, you might have a problem. Remember that point lights require six extra passes, and directional lights three, but both only count as one shadow. You also want your rendered shadow polys to be as low as possible.
    How to fix: Figure out what objects are triggering the redraw and whether it is necessary. Non-moving high-polygon models should use the static shadow mode so they don't have to be redrawn during a render. In version 4.3, low and medium light quality settings will also stagger shadow updates so that fewer are rendered each frame. (This can also make it harder to detect a problem, so maybe test on high quality settings when you are examining this.)
    GPU Pixel Pipeline
    The GPU has a limited number of stream processors it can split up the task of rendering an image with. When you overload the GPU pixel pipeline it slows down your program.
    How to identify: If you have a much higher framerate at a lower screen resolution, this is probably the cause.
    How to fix: Lower light quality settings, remove post-processing effects, or run at a lower screen resolution.
    GPU Vertex Pipeline
    This is pretty rare because the number of vertices the GPU has to process are tiny compared to the number of pixels, but it is possible.
    How to identify: Slow speed regardless of screen resolution, slow even when rendering the scene with no gameplay, extremely high-polygon counts in the game stats (like 2,000,000+). There are some applications where extremely high polygon counts are acceptable, but unless you are specifically making such an application and are aware of this, it probably means you should use models designed for real-time games.
    How to fix: Use lower-resolution models or lighten up on the vegetation.
    Too Many Objects
    The renderer itself has a cost of computation on the CPU. The more separate objects there are, the more the CPU has to work to determine what objects are visible. On the other hand, discarding large numbers of objects can give a big speed boost, so it's always a balance.
    How to identify: The render time shown in your game stats will be more than a few milliseconds, and the number of batches, displayed in the game stats, will be very high. It's all relative but if you have a simple scene and 500 batches are being drawn, there is probably a problem. Large spread out maps with dense distribution of objects can often have this problem. This will happen on all machines, regardless of how good the GPU is. The most complex maps I've ever made had about 700 batches rendered. There is no need to go above that no matter how big the map is, because objects in the distance will be culled. The vegetation system does not cost much on a per object basis, so it is an extremely efficient way to lay down a lot of dense objects.
    How to fix: Use the model editor Collapse feature to collapse models into a single object and resave them. Also set the view range of smaller objects to a closer distance so there are fewer of them visible in the distance.
    Slow Collision
    If your game slows down when you get close to an object, you might have a high-poly collision mesh.
    How to identify: The physics update time in the game stats will be more than a few milliseconds. Enable "View Physics" in the editor and check to make sure all physics shapes are low-poly.
    How to fix: Use the model editor to generate a low-poly physics shape with the best available option.
    Code Errors
    Is your game constantly reloading files from the hard drive? Are you performing lots of pick operations each frame? Did you create a runaway loop of new objects to process?
    How to identify: Comment out sections of your code and test the framerate.
    How to fix: Figure out the main section that is causing the slowdown, then keep commenting out smaller and smaller parts until you narrow down the problem. Post on the forum if you don't know why something is causing a performance drop.
  3. Josh
    The Leadwerks 5 API uses C++11 smart pointers for all complex objects the user interacts with. This design replaces the manual reference counting in Leadwerks 4 so that there is no Release() or AddRef() method anymore. To delete an object you just set all variables that reference that object to nullptr:
    auto model = CreateBox(); model = nullptr; //poof! In Lua this works the same way, with some caveats:
    local window = CreateWindow() local context = CreateContext(window) local world = CreateWorld() local camera = CreateCamera(world) camera:SetPosition(0,0,-5) local model = CreateBox() while true do if window:KeyHit(KEY_SPACE) then model = nil end world:Render() end In the above example you would expect the box to disappear immediately, right? But it doesn't actually work that way. Lua uses garbage collection, and unless you are constantly calling the garbage collector each frame the model will not be immediately collected. One way to fix this is to manually call the garbage collector immediately after setting a variable to nil:
    if window:KeyHit(KEY_SPACE) then model = nil collectgarbage() end However, this is not something I recommend doing. Instead, a change in the way we think about these things is needed. If we hide an entity and then set our variable to nil we can just defer the garbage collection until enough memory is accrued to trigger it:
    if window:KeyHit(KEY_SPACE) then model:Hide()-- out of sight, out of mind model = nil end I am presently investigating the sol2 library for exposing the C++ API to Lua. Exposing a new class to Lua is pretty straightforward:
    lua.new_usertype<World>("World", "Render", &World::Render, "Update", &World::Update); lua.set_function("CreateWorld",CreateWorld); However, there are some issues like downcasting shared pointers. Currently, this code will not work with sol2:
    local a = CreateBox() local b = CreateBox() a:SetParent(b)-- Entity:SetParent() expects an Entity, not a Model, even though the Model class is derived from Entity There is also no support for default argument values like the last argument has in this function:
    Entity::SetPosition(const float x,const float y,const float z,const bool global=false) This can be accomplished with overloads, but it would require A LOT of extra function definitions to mimic all the default arguments we use in Leadwerks.
    I am talking to the developer now about these issues and we'll see what happens.
  4. Josh
    Back around February I started working on a website update that included the following:
    Responsive design everywhere. SSL everywhere. Visual improvement of website. Updated documentation system. Tutorials for C++ programming basics. Update forum software to new major version. Forum moved to new URL. All of that is now pretty much done.  These changes improve the online Leadwerks experience and are independent from the software itself, so it was a good idea to get them done now.
    Since September I've had more time to think about Leadwerks Game Engine 5, and although I am not completely sold on Vulkan, I think it's a good plan.
    Leadwerks 5 is all about performance and user experience with VR as a prime target.
    Multithreaded Architecture
    Separate threads for navmesh updating, physics, game logic, culling, and rendering.  The rendering thread loops at a constant 60 or 90 (for VR) frames per second regardless of what your game is doing.  This gives your game logic four times more time to run, while independently maintaining a constant framerate.  The design I have in mind will make Leadwerks 5 the fastest game engine, ever, and choosing Leadwerks for VR will be a no-brainer.
    Leadwerks Editor 5
    A new editor will be written in C++ using Leadwerks GUI, which will give us the same appearance on Windows, Linux, and Mac.  Functionality will be pretty close to the existing editor, but with more room to grow and a few improvements.  Because it's written in C++ parts of the editor can be exposed to Lua, and an editor API will be provided for making Lua mods and plugins.  By default, it will use a dark theme to be easy on the eyes.  A standalone script editor may be provided as well.
    PBR Material System with Substance Support
    The lighting model will use a more advanced lighting equation and substance PBR materials (metalness and roughness) will be loaded natively.
    Shared Pointers
    The reference counting system in the Object class will be replaced with C++11 shared pointers.  This gives you the performance of C++ with ease of use like a garbage-collected language.
    64-bit
    The engine and editor will be released as a 64-bit build only.
    Game Templates
    More game templates will be provided.  Fortunately we can add these now and updates for Leadwerks 5 will be minimal.
    Open-Source Components
    Source code to some parts of the engine and editor may be provided on the Leadwerks GitHub account.  For example, I may make a standalone open-source script editor or publish some of the engine classes for the community to play with.
    Platforms
    Leadwerks 5 will launch on Windows, Linux, and Mac.  The improved compatibility of Leadwerks 5 means we could do crazy things like run the editor on an iPad, but I'm going to stick with what I know sells.
    Enterprise Edition
    A standalone version that does not use Steam will be sold in bundles to companies that require this.
    Pricing
    A monthly plan may be introduced at around $5-20 per month.  Pricing for a perpetual license for the standard and pro editions would most likely be the same as now ($99-199), with a discount for early adopters / upgrades.  The enterprise version would probably be about $1000 per seat with a discount for schools.
    If you found this blog interesting, please consider using the social media share buttons below to share it.
  5. Josh
    Internally, Leadwerks Editor uses an EventHandler class for every interface in the program. The material editor is a class extended from the EventHandler. So is the little window that has all the controls to calculate normals. So is every viewport.
    The event handler class has one important function:
    Event ProcessEvent(Event) Every EventHandler has access to events as they occur. This is how all program actions are handled in the editor.
    The plugin system will work by hooking into the event system. Each plugin will have a Lua script that receive events before the rest of the program sees them:
    function Script:ProcessEvent(event) return event end If the plugin makes no changes to the event then it simply returns the original event. The returned event is then sent to other event handlers.
    Here is an example of a plugin that would disable the close window button on the main window. Because the function returns nil the event is discarded before the main window ever evaluates it:
    function Script:ProcessEvent(event) if event.id == EVENT_WINDOWCLOSE and event.source == editor.mainwindow then return nil else return event end end Here is an example of a very mean plugin that would make it so that clicking the File > Open menu item in the main window quits the program:
    function Script:ProcessEvent(event) if event.id == EVENT_MENUEVENT then if event.source == editor.mainwindow then if event.extra == MENU_FILEOPEN then event.id = EVENT_WINDOWCLOSE end end end return event end Okay, now let's see if we can design a plugin for something people would actually want. Let's imagine we have a new visual material design system. The exact details of how it works are not important, it's just a system that overrides the default material editor. The design system would require materials to have a special file associated with them with the extension .DESIGN. If you open the material "brick.mat" we will look for a file in the same folder called "brick.design". If the design file is found we open the material in our special editor. If the design file is missing we will just fall back to the default material editor.
    Now let's see how our system can handle this:
    function Script:Start() --Create our interface self.window = CreateWindow("Material Designer",0,0,800,600,editor.mainwindow,WINDOW_CENTER + WINDOW_TITLEBAR + WINDOW_RESIZABLE) end function Script:ProcessEvent(event) if event.id == EVENT_FILEOPEN --Check for material files being opened if ExtractExt(event.extra)=="mat" --Look for design file local designfilename = StripExt(event.extra).".design" if FileType( designfilename ) == 1 then --Load the design file local stream = ReadFile(designfilename) if stream ~= nil then --Display our custom material editor self.window:Show() self.window:Activate() else Print("Error: Failed to load design file.") end --Discard the event return nil end end end return event end As you can see, this approach is extremely powerful. The event IDs and design rarely change, if ever, so this allows a lot of flexibility and at the same time gives us the optimal compatibility as changes are made to the core editor. With this approach to plugins you can literally do anything you want in the editor.
  6. Josh
    During development of Leadwerks Game Engine, there was some debate on whether we should allow multiple scripts per entity or just associate a single script with an entity. My first iteration of the scripting system actually used multiple scripts, but after using it to develop the Darkness Awaits example I saw a lot of problems with this. Each script used a different classname to store its variables and functions in, so you ended up with code like this:
    function Script:HurtEnemy(amount) if self.enemy ~= nil then if self.enemy.healthmanager ~= nil then if type(self.enemy.healthmanager.TakeDamage)=="function" then self.enemy.healthmanager.TakeDamage(amount) end end end end I felt this hurt script interoperability because you had to have a bunch of prefixes like healthmanager, ammomanager, etc. I settled on using a single script, which I still feel was the better choice between these two options:
    function Script:HurtEnemy(amount) if self.enemy ~= nil then if type(self.enemy.TakeDamage)=="function" then self.enemy.TakeDamage(amount) end end end Scripting in Turbo Game Engine is a bit different. First of all, all values and functions are attached to the entity itself, so there is no "script" table. When you access the "self" variable in a script function you are using the entity object itself. Here is a simple script that makes an entity spin around its Y axis:
    function Entity:Update() self:Turn(0,0.1,0) end Through some magic that is only possible due to the extreme flexibility of Lua, I have managed to devise a system for multiple script attachments that makes sense. There is no "component" or "script" objects itself, adding a script to an entity just executes some code that attached values and functions to an entity. Adding a script to an entity can be done in C++ as follows:
    model->AttachScript("Scripts/Objects/spin.lua"); Or in Lua itself:
    model:AttachScript("Scripts/Objects/spin.lua"); Note there is no concept of "removing" a script, because a script just executes a bit of code that adds values and functions to the entity.
    Let's say we have two scripts named "makeHealth100 and "makeHealth75".
    MakeHealth100.lua
    Entity.health=100 MakeHealth75.lua
    Entity.health=75 Now if you were to run the code below, which attaches the two scripts, the health value would first be set to 100, and then the second script would set the same value to 75, resulting in the number 75 being printed out:
    model->AttachScript("Scripts/Objects/MakeHealth100.lua"); model->AttachScript("Scripts/Objects/MakeHealth75.lua"); Print(entity->GetNumber("health")); Simple enough, right? The key point here is that with multiple scripts, variables are shared between scripts. If one scripts sets a variable to a value that conflicts with another script, the two scripts won't work as expected. However, it also means that two scripts can easily share values to work together and create new functionality, like this health regeneration script that could be added to work with any other scripts that treat the value "health" as a number.
    HealthRegen.lua
    Entity.healthregendelay = 1000 function Entity:Start() self.healthregenupdatetime = CurrentTime() end function Entity:Update() if self.health > 0 then if CurrentTime() - self.healthregenupdatetime > self.healthregendelay then self.health = self.health + 1 self.health = Min(self.health,100) end end end What about functions? Won't adding a script to an entity overwrite any functions it already has attached to it? If I treated functions the same way, then each entity could only have one function for each name, and there would be very little point in having multiple scripts! That's why I implemented a special system that copies any added functions into an internal table. If two functions with the same name are declared in two different scripts, they will both be copied into an internal table and executed. For example, you can add both scripts below to an entity to make it both spin and make the color pulse:
    Spin.lua
    function Entity:Update() self:Turn(0,0.1,0) end Pulse.lua
    function Entity:Update() local i = Sin(CurrentTime()) * 0.5 + 0.5 self:SetColor(i,i,i) end When the engine calls the Update() function, both copies of the function will be called, in the order they were added.
    But wait, there's more.
    The engine will add each function into an internal table, but it also creates a dummy function that iterates through the table and executes each copy of the function. This means when you call functions in Lua, the same multi-execution feature will be available. Let's consider a theoretical bullet script that causes damage when the bullet collides with something:
    function Entity:Collision(entity,position,normal,speed) if type(entity.TakeDamage) == "function" then entity:TakeDamage(20) end end If you have two (or more) different TakeDamage functions on different scripts attached to that entity, all of them would get called, in order.
    What if a function returns a value, like below?:
    function Entity:Update() if self.target ~= nil then if self.target:GetHealth() <= 0 then self.target = nil --stop chasing if dead end end end If multiple functions are attached that return values, then all the return values are returned.

    To grab multiple returned values, you can set up multiple variables like this:
    function foo() return 1,2,3 end a, b, c = foo() print(a) --1 print(b) --2 print(c) --3 But a more practical usage would be to create a table from the returned values like so:
    function foo() return 1,2,3 end t = { foo() } print(t[1]) --1 print(t[2]) --2 print(t[3]) --3 How could this be used? Let's say you had a script that was used to visually debug AI scripts. It did this by checking to see what an entity's target enemy was, by calling a GetTarget() function, and then creating a sprite and aligning it to make a line going from the AI entity to its target it was attacking:
    function Entity:UpdateDisplay() local target = self:GetTarget() self.sprite = CreateSprite() local p1 = self.entity:GetPosition() local p2 = target:GetPosition() self.sprite:SetPosition((p1 + p2) * 0.5) self.sprite:AlignToVector(p2 - p1) self.sprite:SetSize(0.1,(p2-p1):Length()) end Now let's imagine we had a tank with a main gun as well as an anti-aircraft gun that would ward off attacks from above, like this beauty I found on Turbosquid:

    Let's imagine we have two different scripts we attach to the tank. One handles AI for driving and shooting the main turret, while the other just manages the little machine gun. Both the scripts have a GetTarget() function, as the tank may be attacking two different enemies at once.
    We can easily modify our AI debugging script to handle multiple returned values as follows:
    function Entity:UpdateDisplay() local targets = { self:GetTarget() } --all returned values get put into a table for n,target in ipairs(targets) do local sprite = CreateSprite() self.sprites.insert(sprite) local p1 = self.entity:GetPosition() local p2 = target:GetPosition() sprite:SetPosition((p1 + p2) * 0.5) sprite:AlignToVector(p2 - p1) sprite:SetSize(0.1,(p2-p1):Length()) end end However, any scripts that are not set up to account for multiple returned values from a function will simply use the first returned value, and proceed as normal.
    This system supports both easy mix and match behavior with multiple scripts, but keeps the script code simple and easy to use. Scripts have easy interoperability by default, but if you want to make your function and variable names unique to the script it is easy to do so.
    Let me know if you have any other ideas for scripting in Turbo Game Engine.
  7. Josh
    Leadwerks 3.1 is nearly ready for release! In Leadwerks 3.0, we focused on making a solid cross-platform art pipeline and editor. In 3.1 we're adding graphics that go above and beyond the capabilities of Leadwerks 2.
     
    New Features in 3.1
    OpenGL 4.0 deferred renderer with up to 32x hardware MSAA.
    Geometry and tessellation shaders.
    Support for the Linux operating system, for both the engine AND editor.

     




    Leadwerks 3.1 is now available for pre-ordering in the Leadwerks store. Existing 3.0 customers can pre-order the upgrade to 3.1 for $99. New customers can pre-order Leadwerks 3.1 for $199. Order before January 6 and get the Indie Edition on Steam for free.
     
    The upgrade to an OpenGL 4 deferred renderer is a big step. To make the process smoother and put Leadwerks in your hands sooner, we're rolling Leadwerks 3.1 out in stages.
     
    "Leadwerks: Indie Edition" will be launched on Steam January 6th. This will be on Windows only, with support for Lua scripting. The following groups will receive a free Steam key to add this product to their Steam account:


    Leadwerks 3.0 customers who pre-order the upgrade to version 3.1.
    New customers who pre-order Leadwerks 3.1.
    All Kickstarter backers who backed Leadwerks for Linux for $49 or more. (Even if you don't run Windows, hold onto this as the Linux version on Steam will have special features.)

     

    Leadwerks 3.1 for Linux and Windows will be released together next, with the exact release date to be determined. Leadwerks 3.1 for Mac will follow this, with mobile add-ons for iOS and Android coming last. (There is no purchase necessary to upgrade the mobile add-ons from Leadwerks 3.0 to Leadwerks 3.1.)


     

    Exemptions


    Leadwerks 3.1 beta testers will receive the 3.1 upgrade for free.
    Leadwerks 3.0 customers who backed the Leadwerks for Linux Kickstarter project for $99 or more will receive the 3.1 upgrade for free.


    Leadwerks 3.1 is a very strong product, with great graphics and a fantastic art pipeline. I'd like to thank all the users, both old and new, who offered their input on product design and the direction of the company. I can't wait to see what the community does with Leadwerks 3.1.


  8. 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.
  9. Josh
    In evaluating possible company names I have come up with the following criteria which I used to choose a name for our new game engine.
    Spelling and Pronunciation
    The name should be unambiguous in spelling. This helps promote word-of-mouth promotion because when someone hears the name for the first time, they can easily find it online. Similarly, the name when read should be unambiguous in pronunciation. This helps the name travel from written to spoken word and back. Can you imagine telling someone else the name of this...establishment...and having them successfully type the name into a web browser?:

    Shorter is Better
    Everything else aside, fewer letters is generally better. Here is a very long company name:

    And here is perhaps the shortest software company name in history. Which do you think is better?

    The Name Should "Pop"
    A good company or product name will use hard consonants like B, T, K, X, and avoid soft sounding letters like S and F. The way a name sounds can actually influence perception of the brand, aside from the name meaning. The name "Elysium", besides being hard to pronounce and spell, is full of soft consonants that sound weak.

    "Blade Runner", on the other hand, starts with a hard B sound and it just sounds good.

    Communicate Meaning
    The name should communicate the nature of the product or company. The name "Uber" doesn't mean anything except "better", which is why the company Uber originally launched as UberCab. Once they got to a certain size it was okay to drop the "cab" suffix, but do you remember the first time you heard of them? You probably thought "what the heck is an Uber?"

    The Leadwerks Brand
    So according to our criteria above, the name Leadwerks satisfies the following conditions:
    The name "pops" and sounds cool. It's not too long. But here's where it falls short:
    Ambiguity in spelling (Leadworks?) Ambiguity in pronunciation. Leadwerks is pronounced like Led Zeppelin, but many people read it as "Leed-works". The name doesn't mean anything, even if it sounds cool. It's just a made-up word. These are the reasons I started thinking about naming the new engine something different.
    New Engine, New Name
    So with this in mind, I set out to find a new name for the new coming engine. I was stumped until I realized that there are only so many words in the English language, and any good name you come up will invariably have been used previously in some other context, hopefully in another industry or product type. Realizing this gave me more leeway, as I did not have to come up with something completely unique the world has never heard before.
    Our early benchmarks indicate the new engine is a performance monster, with incredible results I did not even dream were possible. Together with the rapid development pipeline of Leadwerks, I knew I wanted to focus on speed. Finally, there was one name I kept coming back to for weeks on end. I was able to obtain a suitable domain name. I am now filing a trademark for use of this name, which requires that I begin using it commercially, which is why I am now revealing the name for the first time...
     
     
     
     
     
     
     
     
     
     
     
     
     
     
    Keep scrolling.
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     

    How does this name stack up?:
    Unambiguous spelling and pronunciation. It's short. The name "pops". It communicates the defining feature of the product. Now think about our goals for the new engine's name. Will people have any trouble remembering this name? Is there any ambiguity about what the product stands for, and the promise it makes? If two developers are at a Meetup group and one of them says "I made this with Turbo" is there any doubt what the promise of this product is, i.e. massive performance?
    The name even works on a subconscious level. Anyone having trouble with their game performance (in other slow engines that aren't Turbo) will naturally wonder how fast it could be running in ours.


    The fact that the name has a positive emotional response for many people and a strong connection to the game industry is a plus.
    Turbo Game Engine is an unambiguous brand name that takes a stand and makes a clear promise of one thing: speed, which is incredibly important in the days of VR and 240 hz screens.
  10. Josh
    I've made progress with the new vehicle system and it is shaping up nicely. The vehicle wheels consist of a slider joint with a spring (the strut) connected to a pivot, connected to the wheel by a hinge joint (the axle). If the wheel can be steered, an additional pivot is inserted between the strut and axle, with a motorized hinge to control steering. There were two big problems in addition to this that need to be solved in order to make a vehicle that is stable at high speeds.
    First, the mass matrix of the tires needs to be spherical. The mass matrix is the distribution of mass across an object. A brick and a 2x4 piece of lumber probably have about the same mass, but have a different mass matrix. Therefore the brick should spin more easily than the lumber. If you don't make the mass matrix for the tires spherical you will get bad wobbling at high speeds, like this video shows:
    When the mass matrix is fixed this problem goes away. The vehicle gets up to 90 MPH, and although there are other issues, there is no tire wobble.
    The other issue that needs to be solved can be seen in the video above. At high speeds the tire collisions become inaccurate and the vehicle bounces a lot. We need to replace the default collision with a volume raycast coming from the top position the wheel can sit on the shock, going down to the extended position of the strut. This is the part I haven't done yet, but I know it can be done.
    I think the new vehicle system will offer a lot of flexibility and possibilities for future features since it is mostly made with the standard physics features.
  11. Josh
    A couple weeks ago I replaced the Object::SetHook() function with an Object::AddHook() function. The difference is subtle but significant. SetHook() supported a single hook function but AddHook() supports multiple hook functions for each hook ID, that will be called in sequence.

    Syntax
    AddHook(const int& hookid, void* hook)


    Example
    #include "App.h" using namespace Leadwerks; App::App() : window(NULL), context(NULL), world(NULL), camera(NULL) {} App::~App() { delete world; delete window; } void Hook1(Entity* entity) { System::Print("Hook 1"); } void Hook2(Entity* entity) { System::Print("Hook 2"); } void Hook3(Entity* entity) { System::Print("Hook 3"); } bool App::Start() { window = Window::Create(); context = Context::Create(window); world = World::Create(); camera = Camera::Create(); DirectionalLight::Create()->SetRotation(35,45,0); //Create a model model = Model::Box(); model->SetPosition(0,0,3); //Add some hooks model->AddHook(Entity::UpdateMatrixHook,Hook1); model->AddHook(Entity::DrawHook,Hook2); model->AddHook(Entity::DrawEachHook,Hook3); //Remove one hook model->RemoveHook(Entity::DrawEachHook,Hook3); return true; } bool App::Continue() { if (window->Closed() || window->KeyDown(Key::Escape)) return false; //Make the model spin model->Turn(0,Time::GetSpeed()*1.0,0); Time::Update(); world->Update(); world->Render(); context->Sync(); return true; }
     
    Inside the engine, this just uses a multimap, which has some very complicated syntax that I could not type from memory:

    //Call hooks std::pair <std::multimap<int,void*>::iterator, std::multimap<int,void*>::iterator> ret; ret = (*entity)->hooks.equal_range(Entity::UpdateWorldHook); for (std::multimap<int,void*>::iterator it=ret.first; it!=ret.second; ++it) { void (*hook)(Entity*) = (void (*)(Entity*))(it->second); hook(*entity); }

    So What's the Big Deal?
    It's important for the API to be future-compatible. It's okay for new commands to be added, but once the first release is out I really want the API to always act as described in the documentation. We wouldn't be bothering to produce a printed manual otherwise. 
    This design was adopted so that in the future it can be used for plugins. A plugin would add its own hooks onto objects. If we used SetHook(), it implies there is only a single hook allowed. We want the user to be able to add their own hooks without overriding any plugins they may be using. This also allows multiple plugins to add and remove their own hooks without interfering with one another.
     
    It's too early to worry much about a plugin system. The best thing to do right now is build a really focused tool that fulfills the function it's designed for. However, by anticipating plans for future expansion we can design things now so that we don't have to go back and change them later.
  12. Josh
    I've spent the last few days writing simple examples for every single command in Leadwerks 3. Not only does this make the documentation more friendly, it also acts as a final test to make sure all the commands work the way they say they should. I make the C++ example and then Chris converts it to Lua (and tells me what I did wrong!).
     
    I didn't realize it at first, but this really showcases the strength of API design of Leadwerks. Since you get full control over the execution and flow of a Leadwerks program, it's easy to learn from simple examples that demonstrate one idea. Below are a few examples for different commands in the API.
     
    Get the device accellerometer reading:

    #include "App.h" using namespace Leadwerks; Window* window = NULL; Context* context = NULL; bool App::Start() { window = Window::Create(); context = Context::Create(window); return true; } bool App::Continue() { if (window->Closed() || window->KeyDown(Key::Escape)) return false; Draw::SetColor(0,0,0); context->Clear(); //Display the device information on the screen Draw::SetBlendMode(Blend::Alpha); Draw::SetColor(1,1,1); Draw::Text("Orientation: "+String(Device::GetOrientation()),2,2); Draw::Text("Acceleration: "+Device::GetAcceleration().ToString(),2,22); Draw::SetBlendMode(Blend::Solid); context->Sync(); return true; }
     
    Create a physics shape from a model and use it on a scaled entity :

    #include "App.h" using namespace Leadwerks; Window* window = NULL; Context* context = NULL; World* world = NULL; Camera* camera = NULL; bool App::Start() { window = Window::Create(); context = Context::Create(window); world = World::Create(); camera = Camera::Create(); camera->SetRotation(35,0,0); camera->Move(0,0,-10); Light* light = DirectionalLight::Create(); light->SetRotation(35,35,0); //Create the ground Model* ground = Model::Box(10,1,10); ground->SetPosition(0,-0.5,0); ground->SetColor(0,1,0); //Create a shape Shape* shape = Shape::Box(0,0,0, 0,0,0, 10,1,10); ground->SetShape(shape); shape->Release(); //Load a model Model* model = Model::Load("Models/teapot.mdl"); model->SetPosition(0,0,0); model->SetColor(0,0,1); model->SetScale(4,4,4); //Create a shape shape = Shape::PolyMesh(model->GetSurface(0)); model->SetShape(shape); model->SetPosition(0,0,0); shape->Release(); //Create some objects to fall model = Model::Sphere(); shape = Shape::Sphere(); model->SetShape(shape); shape->Release(); model->SetMass(1); model->SetColor(Math::Rnd(0,1),Math::Rnd(0,1),Math::Rnd(0,1)); model->SetPosition(Math::Rnd(-1,1),Math::Rnd(3,6),Math::Rnd(-1,1)); for (int i=0; i<10; i++) { model = (Model*)model->Instance(); model->SetCollisionType(Collision::Prop); model->SetColor(Math::Rnd(0,1),Math::Rnd(0,1),Math::Rnd(0,1)); model->SetPosition(Math::Rnd(-1,1),5+i*2,Math::Rnd(-1,1)); } return true; } bool App::Continue() { if (window->Closed() || window->KeyDown(Key::Escape)) return false; Time::Update(); world->Update(); world->Render(); context->Sync(); return true; }
     
    Or in Lua, if you prefer:

    function App:Start()  
    self.window=Window:Create(self.title,0,0,1136+6,640+32,Window.Titlebar+Window.Center+8)
    self.context=Context:Create(self.window,0)
    self.world=World:Create()
    camera = Camera:Create()
    camera:SetRotation(35,0,0)
    camera:Move(0,0,-10)
    light = DirectionalLight:Create()
    light:SetRotation(35,35,0)
     
    --Create the ground
    ground = Model:Box(10,1,10)
    ground:SetPosition(0,-.05,0)
    ground:SetColor(0,1,0)
     
    --Create a shape
    shape = Shape:Box(0,0,0, 0,0,0, 10,1,10)
    ground:SetShape(shape)
    shape:Release()
     
    --Load a model
    model = Model:Load("Models/teapot.mdl")
    model:SetPosition(0,0,0)
    model:SetColor(0,0,1)
    model:SetScale(4,4,4)
     
    --Create a shape
    shape = Shape:PolyMesh((model:GetSurface(0)))
    model:SetShape(shape)
    model:SetPosition(0,0,0)
    shape:Release()
     
    --Create some objects to fall
    model = Model:Sphere()
    shape = Shape:Sphere()
    model:SetShape(shape)
    shape:Release()
    model:SetMass(1)
    model:SetColor(Math:Rnd(0,1),Math:Rnd(0,1))
    model:SetPosition(Math:Rnd(-1,1),Math:Rnd(3,6),Math:Rnd(-1,1),true)
     
    for i=0, 9 do
    model = tolua.cast(model:Instance(),"Model")
    model:SetCollisionType(Collision.Prop)
    model:SetColor(Math:Rnd(0,1),Math:Rnd(0,1),Math:Rnd(0,1))
    model:SetPosition(Math:Rnd(-1,1),5+i*2,Math:Rnd(-1,1),true)
    end
    return true
    end
     
    function App:Continue()
     
    if self.window:Closed() or self.window:KeyHit(Key.Escape) then return false end
     
    Time:Update()
    self.world:Update()
    self.world:Render()
    self.context:Sync()
     
    return true
    end
     
    Create a texture from scratch:

    #include "App.h" using namespace Leadwerks; Window* window = NULL; Context* context = NULL; World* world = NULL; Texture* texture = NULL; bool App::Start() { window = Window::Create(); context = Context::Create(window); //Create a texture texture = Texture::Create(256,256); //Set the texture pixel data char* pixels = (char*)malloc(texture->GetMipmapSize(0)); char r,g,b; for (int x=0; x<256; x++) { for (int y=0; y<256; y++) { int p = (x*texture->GetWidth() + y)*4; memcpy(&r,pixels + p + 0, 1); memcpy(&g,pixels + p + 1, 1); memcpy(&b,pixels + p + 2, 1); if (x<128) { if (y<128) { r=0; g=0; b=255; } else { r=255; g=0; b=0; } } else { if (y<128) { r=255; g=0; b=0; } else { r=0; g=0; b=255; } } memcpy(pixels + p + 0, &r, 1); memcpy(pixels + p + 1, &g, 1); memcpy(pixels + p + 2, &b, 1); } } texture->SetPixels(pixels); return true; } bool App::Continue() { if (window->Closed() || window->KeyDown(Key::Escape)) return false; Draw::SetColor(0,0,0); context->Clear(); //Display the texture on screen Draw::SetColor(1,1,1); Draw::Image(texture,0,0); context->Sync(); return true; }
  13. Josh
    I've made more progress on the model editor. I needed it to be able to resave models after making changes. First, I started with an Entity::Save function that would save all entities in the GMF format. "But wait", I thought, "I can save all entities in the GMF format!" Then I thought, even better, I can simply write a Serialize() virtual class function in the base object and extend it for each class, so that instead of writing this:

    stream->WriteFloat(position.x); stream->WriteFloat(position.y); stream->WriteFloat(position.z);
    I can simply write this:

    position.Serialize(stream);
    "I am so smart! I am so smart! S-M-R-T!"
     
    Wait a minute, if I write a World::Serialize function, I could save an entire world with all entities in a single file, so that another person can just open the file and recreate everything that was happening! Amazing!
     
    Of course, serializing every instance of every texture would be terribly wasteful. Should assets still be loaded from files? What if they were missing? What about models that were loaded and then modified? Can I use this for sending entities over the network? How does this all fit together?
     
    It was around that time my conscience started kicking in, or whatever that feeling you get when you know you're doing something dumb is. So I stopped what I was doing, imported the GMFSDK code into the editor, and wrote a SaveModel() function, which is all I needed in the first place.
     
    I changed the default background color to dark gray (0.25). It makes thumbnails more easily visible, and is dark enough to not kill your ability to see dark pixels. The thumbnail generation uses the last rotation you had in the model editor, so it's very easy to adjust the thumbnail. The thumbnail renders use an orthographic view, which makes it easier to make better use of the available pixel space, and seems to look good. Thumbnail renders for materials and models use 2x antialiasing, which looks great blended against the background color.
     
    The thumbnail icon view we use for assets is one of my favorite parts of the editor. Having used this and a few alternatives for a while, I think this gives you much faster and easier access to all your files.
     

  14. Josh
    In the next beta update, the way entity fields work in the script properties will be a little different. Instead of dragging an object from the scene tree onto the field, you will type in the name of the object (or copy and paste the name from the object's name field). This is being done to make the behavior more intuitive. After working with both approaches, I find the new way much easier to use.
     

     
    This does however mean that an object that is used in this manner must have a unique name, since that is what is used to make the linkage. Additionally, prefab limbs can now have their names changed without breaking the prefab, in order to accommodate this design.
     
    Your existing maps will be unaffected until they are resaved in the editor, but if you have multiple target objects with the same name, your map will need to be updated.
     
    Alos, the texture list in the material editor is being reverted back to its original design, which was a bit more obvious in its presentation and usage.
     

  15. Josh
    The development of Leadwerks3D seemed like an impossible task at first. Not only did I need to write a new 3D engine entirely in C++ mostly by myself, but I had to make it run on four platforms (Android, iOS, Windows, and Mac), with a scalability all the way from the fixed-function pipeline up to the very latest hardware tessellation features. All logical sense told me this was impossible. Even large companies with 100 programmers or more haven't yet been able to put out a 3D engine with the range of platforms and scalability I am targeting. What makes me think I can do it?
     
    As we approach the initial release of Leadwerks3D, what I thought was impossible seems much more feasible now. During the course of development of Leadwerks3D, I've learned some important ideas that give a lot of hope to me and to indie game developers everywhere.
     

    Systemic Interdependence
    When managing complex tasks there's something called systemic interdependence. This refers to the idea that when you have many people working on one project, they are rarely isolated from one another. They often have to stop and wait for another person's job to be done before they can continue with their own, and programmers often end up stepping on each other's toes when they work together. 
    Even though only Aria and I are working on the source right now, we have experienced this. It's very important we keep busy with separate aspects of the engine. Aria works on platform implementation issues, I work on the core engine design, and we try our best to keep the source code repository in sync. So far this works well for us, but I could easily see a lot of problems arising if we had more people working on parts of the engine that aren't so easily compartmentalized.
     
    Think about your own game. What if I told you I was willing to give you as much money as you wanted to hire programmers, (as long as your final project gave me a big return on my money). Sound great, right? So you go out and hire ten programmers. They come in every weekday, 9 to 5. They're willing to work, but you have to instruct them and keep them busy and productive. Don't have anything for them to do at the moment while you finish one little part? Too bad, they're still on the clock and you still have to pay them.
     
    With ten programmers, would your game get done ten times faster? Almost certainly not. In fact, as you add programmers, your work output will probably look something like the graph below. With one programmer, we have an output of 100 arbitrary units. We gain efficiency with the first few programmers, but the effect lessens with each additional worker. They have to wait for another person to finish something. Now the code repository is conflicted, and you have to figure out how who added what code. You also have an increasing number of relationships and communication between individuals.
     

     
    Once we get past five programmers, additional programmers actually have a detrimental effect to our output. If we continue adding more workers, we can reach levels of output that are below even that of a single programmer. Imagine if 30 people were working on your game. It would be chaos, and that whole time you would be burning money as they came in every day. Meanwhile, your investors would be sitting impatiently, expecting you to get back a lot more money than they put in.
     
    My chart above isn't precise, and the number of programmers a project can support will vary based on its ease of compartmentalization, but the overall idea stands: Software development does not scale very well with additional manpower. This is why small companies are continually springing up and outmaneuvering larger ones. Android came from a small company. It was bought by Google, but only after a small team had built the foundation. Minecraft came from a very small company. Kinect originally came from a small company. Small companies with focused goals are able to compete with much larger companies because of the effect of diminishing returns.
     

    Performance and Motivation
    Another problem big companies have is motivation. In a professional software development environment, managers like to have measurable performance metrics to evaluate employee efficiency. These are used for performance evaluations, and are the basis for salary increases, promotions, and disciplinary actions. Managers set goals and milestones that can be easily measured after a period of time. Workers respond to this by doing exactly what management tells them because they get rewarded for it. If a worker has an idea for a new technique that might or might not work, they're not as likely to spend time on it in this environment. If it works, the company gets to keep their idea and they get nothing. If it fails, they've wasted time on something that doesn't fit into their manager's ideas of measurable performance. Now, a lot of tech companies do try to encourage innovation, but if a worker has an idea for something they really believe in, they are more likely to form their own company where they can be in complete control of their vision. The professional software development environment does not encourage risk-taking and innovation the same way small companies do, because they need easily measurable metrics to assess performance. They only get out of employees what they reward, and it's hard to measure the value of new ideas and attention to detail. 

    Conclusion
    These ideas I've learned during the development of Leadwerks3D are directly applicable to your own game projects. Don't get discouraged when you run into a tough problem. Even if you had more help, you would still have to figure out a solution, one way or another, and you wouldn't be able to shift that responsibility off onto employees. You can do it, just be wise about where you focus your efforts and plan carefully. 
    --Edit--
     
    I posted this article on GameDev.net and got a few interesting responses:
     
  16. Josh
    In this blog I'm going to explain the evolution of the entity and physics system in Leadwerks 3.
     
    In Leadwerks Engine 2, physics bodies and character controllers are both separate entity classes. If you want a model to be physically interactive, you parent it to a body entity. If you want a model to walk around with physics, you parent it to a character controller body.
     
    In Leadwerks 3 I decided to give physics properties to all entities. This means all entities can use commands like GetVelocity(), AddForce(), SetMass(), etc. However, since character controllers are quite different, and they involve kind of a big chunk of code, I decided to keep the character controller as a separate entity. To make an enemy or NPC, you would create a character controller entity and then parent an animated model to that entity.
     
    This was simple enough to do in the editor, but it started getting weird when we added scripts. Scripts for animation would need to be added to the child model, because the character controller would not return any animation lengths or the number of sequences. Scripts to control movement, on the other hand, would have to be attached to the parent character controller, for obvious reasons.
     
    Next I tried creating a character controller script that attached to the model itself. This eliminated the extra entity in the hierarchy, and would automatically create a character controller when loaded in the engine, and parent the model to it. I didn't like that this was changing the hierarchy from what the user saw in the editor, and script accessing the character controller would still be based on some wonky assumptions.
     
    Finally, I decided to just give the entity class a physicsmode member. This can be one of two values. By default, it is Entity::RigidBodyPhysics. However, you can set it to Entity::CharacterPhysics and the entity itself will act as a character controller! All the character controller functions are now available in the entity class, so you can just load a model, adjust some settings, and send him on his merry way around town:

    Model* enemy = Model::Load("Models/Characters/barbarian.mdl"); enemy->SetMass(10); enemy->SetPhysicsMode(Entity::CharacterPhysics); enemy->GoToPoint(20,0,0);//model will walk to this position, using AI navigation
     
    Pretty cool, eh? (If you wanted to add animation, you'd just go it like below.)

    enemy->SetHook(Entity::DrawHook,UpdateEnemyAnimation) void UpdateEnemyAnimation(Entity* entity) { entity->SetAnimationFrame(Time::GetCurrent()/100.0); }
  17. Josh
    A new update is available with the following changes:
     
    Player collision against terrains is now working really nicely. In general, the player controller has become very solid and accurate. It's also quite a bit faster than before.
     
    An experimental feature lets you make CSG brushes the child of another object and check a "shape hint" option in the physics properties. This will use any attached CSG brushes to build a new compound convex hull for that model. This is best used to make prefabs, so that the same collision shape gets reused for each instance of the object.
     
    The Code::Blocks C++ project template is updated. You should add the preprocessor definition LUA_USE_LINUX to the root target in your C++ projects.
     
    I started using banks instead of STL vectors for terrain data. I have no hard evidence, but I suspect vectors aren't up to the task of handling the large data terrains involve.
     
    The scene tree now renders at a speed pretty much independently from the number of objects in the scene. I believe this will also fix some weird graphical glitches we've seen on Ubuntu.
     
    The map file format version is incremented. You will need to update your executable to be able to load the new format.
     
    The model and map editor will now free cameras and lights used when their window is hidden, to free up extra video memory.
     
    Map backups have a more logical naming scheme now so it's easy to tell which map is which.
     
    The program log is now stored in "Documents/Leadwerks/Leadwerks.log".
     
    For recent bug fixes, just see recently locked topics in the bug reports forum:
    http://www.leadwerks.com/werkspace/forum/80-bug-reports/
  18. Josh
    An update is available on the beta branch on Steam. This only updates the compiled executables for Lua, only on Windows.

    Optimization
    I've rewritten the way lights and objects affect each other. In the old mobile renderer it was necessary to store a list of lights that affect each entity, because it was using a forward renderer and had to send the information for the nearest four lights to the object's shader to calculate lighting. This was a complicated task and is not needed with a deferred renderer. 
    The C++ PlayAnimation routine had a bug that would cause the animation to keep updating invisibly, causing shadows to continue rendering when they did not need to be. This is fixed.
     
    I also fixed some inefficiency that would affect point and spot lights, even if their shadows were not being updated.
     
    Finally, I set it so that in medium and low quality lighting modes, only one shadow will be updated per frame, unless the player is close to the shadow volume. This allows you to have many lights updating but only one gets their shadowmap redrawn each frame. This can cause some small visual glitches but the high quality setting (World:SetLightQuality(2)) can be used to always render everything.
     
    Performance Benchmarks (4.3 beta vs 4.1/4.2)
    One more Day: 15% faster
    A Demon's Game: 17% faster
    The Garden: 20% faster
    Vectronic: 23% faster
    AI and Events map: 200% faster

     
    This build should be considered unstable. It will crash on some games.
  19. Josh
    The last few weeks have been interesting as we've been figuring out where Leadwerks 3 fits into the game development landscape.
     
    The good:
    The feedback from the Leadwerks 3 users has been more positive than anything I've ever seen. People love using it. All the care and attention to the tools has really paid off. The return of constructive solid geometry is a huge win.
    At the upgrade pricing, we're doing a lot of sales. Leadwerks 3 makes more in a week than Leadwerks 2 made in a month, at its best. (Our best sales are typically in the middle of the product life cycle, not the beginning.)
    Leadwerks 3 has a much wider appeal than anything we've ever made. We're no longer trapped in a small niche, but we still have the expandability to reclaim the crown of high-end graphics.

     
    The bad:
    Indie developers can't afford the full price we hoped to set. It doesn't matter what features we add to the product, there's a certain price threshold we can't cross without a multi-million dollar marketing budget.
    Although our documentation is excellent, people still need a full "how to make a game" series of tutorials. The "Darkness Awaits" example game isn't enough, without being put into a tutorial format.

     
    My original idea for Leadwerks 3 was to make a premium product, with a premium price. I was counting on revenue to come primarily from existing Leadwerks users, because it's easier to sell to an existing customer than to attain a new one. However, it's clear that Leadwerks 3 has mass appeal like nothing we've ever done, and a lot of potential for growth if we price it right. Indie developers are happy to buy Leadwerks 3 at an affordable price point. I think there are enough indie developers out there to support us, so we're making the final price $199/199/199 across the board for new customers. Our other market segment are pro game studios, and for them we can offer source code licenses (at a much higher price). Selling a binary license that's too expensive for indies and too cheap for studios is an awkward positioning, and I don't think it works.
     
    When we get back from the GDC, Chris is going to be taken off the core engine development and put exclusively on end user lessons. We are planning a series of weekly tutorials taking you through the steps to make a full first-person shooter game with Leadwerks 3. We chose this genre because we think the precision of the controls maximizes the potential for interactions, which is where Leadwerks 3 can really shine. I will be focused on core engine and editor fixes, and new features.
     
    Leadwerks 3 has a wide enough appeal that I think we can count on growth to fuel the company, not high prices. Leadwerks 3.1 will be the next stop. I know graphical enhancements are a high priority for a lot of people, as well as gameplay features and editor enhancements.
     
    It is an exciting time when the only limits you have are the size of your ideas and the degree of your dedication. We're already seeing some incredible stuff come from the community with Leadwerks 3.
  20. Josh
    An update is now available on the beta branch which adds water. This can be controlled in the scene properties or in code with the following commands:

    World::SetWaterMode(bool mode) World::SetWaterHeight(float height) World::SetWaterColor(float r, float g, float b, float a) bool World::GetWaterMode() float World::GetWaterHeight() Vec4 World::GetWaterColor()
     
    You must update your project to get new EXEs and shaders. All model shaders have been modified to handle the slice plane used when rendering reflections. A new set of textures have also been added in "Materials\Effects\Water" to handle the surface perturbation.
     
    Caveats / Notes
    When underwater, no post-processing screen wobble or fog is applied like in Leadwerks 2. This is because Leadwerks 3 has a customizable post-process stack, so the end user can control this.
    The water physics are not 100% done and behave oddly.
    There is a bug where switching projects does not reset the water settings to the default.
    At this time there are no water quality settings, though the renderer uses the lowest possible quality in the reflection pass.
    This build has been tested on every hardware / OS configuration except AMD on Linux.

     

  21. Josh
    First, I was experiencing some crashes due to race conditions. These are very very bad, and very hard to track down. The problems were being caused by reuse of thread returned objects. Basically, a thread performs some tasks, returns an object with all the processed data, and then once the parent thread is done with that data it is returned to a pool of objects available for the thread to use. This is pretty complicated, and I found that when I switched to just creating a new return object each time the thread runs, the speed was the same as before. So the system is nice and stable now. I tend to be very careful about sharing data between threads and only doing it in a prescribed manner (through a command buffer and using separate objects) and I will continue to use this approach.
    Second, I added a built-in mouselook mode for cameras. You can call Camera::SetFreeLook(true) and get a automatic mouse controls that make the camera look around. I am not doing this to make things easier, I am doing it because it allows fast snappy mouse looking even if your game is running at a lower frequency. So you can run your game at 30 hz, giving you 33 milliseconds for all your game code to complete, but it will feel like 60+ hz because the mouse will update in the rendering thread, which is running at a faster speed. The same idea will be used to eliminate head movement latency in VR.
    Finally, I switched the instance indexes that are uploaded to the GPU from integers to 16-bit unsigned shorts. You can still have up to 131072 instances of a single object, because the engine will store instances above and below 65536 in two separate batches, and then send an integer to the shader to add to the instance index. Again, this is an example of a hard limit I am putting in place in order to make a more structured and faster performing engine, but it seems like the constraints I am setting so far are unlikely to even be noticed.
    Animation is working great, and performance is just as fast as before I started adding it, so things are looking good. Here's a funny picture of me trying to add things to the renderer to slow it down and failing :

    I'm not sure what I will tackle next. I could work on threading the physics and AI, spend some time exploring new graphics options, or implement lighting so that we have a basic usable version of Leadwerks 5 for indoors games. What would you like to see next in the Leadwerks Game Engine 5 Alpha?
  22. Josh

    Articles
    I've been working hard getting all the rendering features to work together in one unified system. Ultra Engine, more than any renderer I have worked on, takes a lot of different features and integrates them into one physically-based graphics system. A lot of this is due to the excellent PBR materials system that Khronos glTF provides, and then there are my own features that are worked into this, like combined screen-space and voxel ray traced reflections.
    Anyways, it's a lot of difficult work, and I decided to take a "break" and focus something else for a few days.
    Before Leadwerks was on Steam, it had a web installer that would fetch a list of files from our server and any files that were missing, or were newer than the locally stored ones. There was no system for detecting updates, you just pressed the update button and the updater ran. The backend for this system was designed by @klepto2 and it functioned well for what it needed to do.

    With Ultra App Kit, I created a simple tool to generate projects. This introduced the account authentication / signin system, which was sort of superfluous for this application, but it was a good way to test it out:

    With Ultra Engine, I wanted some characteristics of both these applications. I wrote a new backend in about a day that handles updates. A PHP script authenticates the user and verifies product ownership, fetches a list of files, and retrieves files. Since C++ library files tend to be huge, I found it was necessary to add a compression system, so the script returns a zip compressed file. Of course, you don't want the server to be constantly creating zip files, so it caches the file and updates the zip only when I upload a new copy of the file. There's also a cache for the info retrieval, which is returned in JSON format, so it's easy to read in C++.
    For the front end, I took inspiration from the Github settings page, which I thought looked nice:

    And here's what I came up with. Projects will show when they are outdated and need to be updated (if a file in the template the project came from was changed). Each of the sections contains info and links to various topics. There's a lot there, but none of it feels extraneous to me. This is all made with the built-in GUI system. No HTML is used at all:

    The Invision Power REST API is extremely interesting. It allows authentication of accounts and purchases, but it can be made to do a lot of other things.
    Post a forum topic: https://invisioncommunity.com/developers/rest-api?endpoint=forums/topics/POSTindex Upload an image to the gallery: https://invisioncommunity.com/developers/rest-api?endpoint=gallery/images/GETindex Download a file: https://invisioncommunity.com/developers/rest-api?endpoint=downloads/files/GETitem Unlock achievements: https://invisioncommunity.com/developers/rest-api?endpoint=core/members/POSTitem_achievements_awardbadge None of that is very important right now, but it does provide some interesting ideas for future development of the game engine.
  23. Josh
    I feel like you guys are owed an explanation of our long-term strategy, and now that I have definite plans I am happy to reveal them to you.
     
    I've been spending a lot of time in the Silicon Valley area, and have learned a lot about business. We've been investigating external investment. I believe we could raise pretty much any amount of money we want, based on the fact we already have an existing business that is self-sustaining, and we have a great strategy. However, money does not necessarily equal success, and funding brings more restrictions. If we raised $10 million, our investors would expect a $100 million return, and everything we did would have to be geared towards that. If you recall the Blade3D story, you know what can happen when these deals go bad. I believe the wisest strategy for the development of Leadwerks 3.0 is for me to buckle down and write the majority of the C++ code, then add additional programmers once the foundation is in place. This allows me to carefully design the core functionality without external pressures. It ensures I don’t trust the engine core to some programmer that may not understand our design objectives. Finally, it forces me to tame that beast that is C++. Even if my future is more of a management role than programming, I still need to be able to evaluate future employees’ work. Once I feel the project is ready for more programmers, we may seek funding, but we’ll be in a less risky position at that point.
     
    Another thing I have learned is how great networking is. It’s fun, and it can lead to valuable contacts. I live two hours from San Jose, so there is no excuse for me not to be more involved in the game industry. From now on, I am going to attend more IGDA and other events. I may find some good programmers to hire later on, or it may lead to new partnerships like the one I will be announcing with version 2.4. For many developers, Leadwerks is your portal into the game industry. If I am more in involved with what is going on, then by extension you will be, too.
     
    Leadwerks 3.0 will be written in C++, but will still support all the languages we do now. Every part of the code that interfaces with the hardware will be abstracted out as a “driver”. This includes graphics, sound, the file system, and networking. To add support for a new platform, we will just write a new set of drivers for that hardware. The editor will continue to be written in BlitzMax because development time will be shorter and the end result will be fast and cross-platform compatible. To begin with, I am most interested in Windows and Android, but eventually plan on supporting everything. The abstracted driver design we are using makes it possible for separate teams to work on porting the code independently.
     
    I’ve received a lot of great feedback from the community, especially on the tools and workflow. The design of version 3.0 makes it easier for users to just click and drag some items around to make a game, but you can still drill down to the script and programming level, when you need more control. The most intriguing aspect is how it lets advanced programmers work together with designers, and they all can make valuable contributions to a project.
     
    Leadwerks Engine 2.4 will be out soon, and will include a brand new lighting feature that has never been done before, by any engine. The bug tracker is presently clear of reports, but if anything comes up in the future, it will be fixed and patched. After 2.4 is released, I will be going on a short vacation, and when I return work on Leadwerks 3.0 will begin in earnest. We will begin offering the 3.0 beta for sale at a generous discount only to existing Leadwerks Engine 2 developers. When will it be done? I don't know, but I will have a better idea after working with the C++ code for a while.
  24. Josh
    Let's start with some code for making instances and unique copies of a material:

    Material* mat1 = new Material; mat1->SetColor(0,0,1,1); Material* mat2 = mat1->Copy(true); Material* mat3 = mat1->Copy(false); mat1->SetColor(1,0,0,1);
    mat1 and 2 will be red. mat3 will be blue. Shaders, textures, and entities work the same way.
     
    Drawing commands are in. I like how OpenGL3 gets rid of all the built-in matrix stuff and just lets you deal with pure matrix multiplication. It would probably be pretty difficult for a beginner to get into, but it's much cleaner, and it forced me to learn a little more about matrices. I added a mat4 orthogonal projection class function, if you're interested in that sort of thing.
     
    I don't have DrawImage(), SetBlend(), SetColor(), etc. commands in, because the material system can handle all of that, and it's much more powerful. Here's some sample code:

    Material* mat = LoadMaterial("myimage.mat"); mat->SetColor(1,0,1,0.5); mat->SetBlend(BLEND_ALPHA); mat->Enable() DrawRect(2,2,10,20); mat->Disable()
    You can also draw polygons onscreen if you want. Vertex positions will correspond to screen coordinates:

    Material* mat = LoadMaterial("myimage.mat"); Surface* surf = CreateSurface(); surf->AddVertex(0,0,0); surf->AddVertex(0,1,0); surf->AddVertex(0,1,1); surf->AddTriangle(0,1,2); mat->Enable() surf->Draw() mat->Disable()
    There are two types of multisampling in OpenGL. The older technique uses the graphics window pixel format. The newer technique involves an FBO with a multisample format. I am going to disable the first technique, because normally rendering is performed on an FBO (a Leadwerks "buffer") and you don't want multisampling to mess up your 2D drawing that is typically done after 3D rendering. It also prevents the user from having to recreate a graphics window to toggle antialiasing on and off. So to sum that all up, antialiasing should be as simple as just defining a multisample format when you create a buffer, which can be 1,2,4,8, or 16.
     
    I also hired an outside developer to research fluid simulations for ocean water. Here is an early prototype. There's still some improvement to make, but the technique is promising:


     
    On to the editor. Here's the prototype. You can see the asset tree on the right, which functions pretty much like Windows Explorer:

    You can enter a word in the search box, press enter, and the results are instantly filtered. I was surprised at the speed, even with thousands of files:

    You can right-click on a source art asset and convert it to a final game-ready file format. Here we have a png file you can convert to DDS:

    And the familiar DDS convert dialog will appear:

    If you choose the "Reconvert" option, the converter will be run with the last options used to convert that file, without pulling up the options dialog. These settings are stored in a file with the image file, and will be remembered across editor sessions.
     
    One of the coolest features is that the editor automatically detects file changes and will ask you to reconvert a file. Or if you prefer, you can set the editor to always perform conversions automatically.

    Overall, I myself am surprised at the speed with which Leadwerks Engine 3 is taking shape. It's still hard to say exactly when it will be ready, because little details can pop up and take more time, but it's going well.
  25. Josh
    This is the final output of Leadwerks 3 on Windows.
     
    When you choose "Windows" in the Publish dialog, a few options appear. Press OK and your game's installer is created.

     
    The installer looks like this. You can replace the installer images with your own if you like:



×
×
  • Create New...