Jump to content

Josh

Staff
  • Posts

    23,225
  • Joined

  • Last visited

Blog Entries posted by Josh

  1. 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); }
  2. Josh
    PC towers have been about the same since they became popular in the 1990's, replacing the previous horizontal form factor:

     
    Cases have gone from beige to black, but very little else has changed.

     

     
    PC towers tend to be designed for the old ATX style motherboards, which were first introduced in 1995:

     
    PC cases are still designed primarily for this form factor, ignoring the fact that many internal components have changed.
    The sound and ethernet card are now typically built into the motherboard.
    Motherboards that support full-power components can now be purchased cheaply in the Mini-ITX form factor, with an area of just 6x6 inches.
    Hard drives have shrunk in size.
    3.5" floppy drives are long gone, yet most cases still include a slot for these.
    Optical drives aren't even necessary anymore, although they are still the most cost effective and reliable solution for data backup.
    Graphics cards, unlike other components, have gotten bigger, hotter, and thicker. I'm not sure a GPU can even be referred to as a "card" any more. With giant heat sinks, they're more like a "graphics block".

     
    Clearly, an update to the desktop form factor is needed to respond to these changes. There's been a lot of interesting developments in desktop / home theater miniaturization, which involves making a PC with "good enough" specs and a much reduced form factor. The Steam Machines have driven some of this interest, but it's been going on for a while. The utlimate result of this trend is the Gigabyte Brix Pro. Any smaller than this, and it would be a choking hazard:

     
    What about the full-size desktop PC though? Can we find a design that uses full-size components, but packs it into a form factor more suitable for modern hardware design? I set out the following critera when starting this project:
    The case must support a full-size PSU and GPU.
    The GPU must be easily accessible for quick changing.
    At least two hard drives and an optical drive.
    Airflow sufficient for the big hot PSU and GPU it would contain.

     
    Most Mini-ITX cases are designed for small home theater setups, and do not support full-size components. A few can accommodate a full-length GPU, but do not support adequate cooling or power requirements. The Antec ISK600, for example, is a disaster waiting to happen, with a closed side blocking airflow to the GPU:

     
    Other cases may have adequate cooling, but are too tightly packed for frequent GPU switching:

     
    The Bitfenix Prodigy is one popular case I considered, but it's not much smaller than a micro-ATX tower. When you consider the wider footprint of the case, the space savings are debatable:

     
    In the end, there was only one case that offered the hardware support, cooling capacity, and ease of access I was looking for. I give you the Cooler Master Elite 130:

     
    This case packs all my previous components into a volume a little smaller than a breadbox. The lack of a heavy steel case makes the machine much lighter, and the low profile feels safer than a heavy tower I am afraid could tip over at any minute. The GPU and PSU have better access to cool air from the exterior, and a large fan in the front blows air through the entire machine. An optical drive fits seamlessly into the case, under which is a clever mechanism for an SSD to hang sideways.
     
    On the down side, the front of the case under the optical drive is a mess of cables that impede airflow from the front fan. Removing the GPU requires a stick to press the release latch. Removing the SSD requires removal of the PSU, unless you have a very short screwdriver. I also broke the USB pins on my Zotac motherboard, but it's only $50 to replace.
     
    As an added bonus, my motherboard has built-in wifi connectivity. Coupled with the extreme portability of this unit, I am sure that will come in handy at some point.
     
    The net result is that my desktop PC is now portable in a way it's never been before. I could realistically take this with me on a weekend trip or to a LAN party, whereas I would never consider doing this with my tower. At last, we're seeing real technological innovation that isn't just a reduction of capabilities, but a real move forward towards something that was better than the old way.
     

  3. 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.
  4. 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.
     

  5. Josh
    I'm happy to announce the very first alpha release of Leadwerks 5 is now available.
    What's New
    String commands now accept a unicode overload. Add "L" in front of a string to create a wide string in C++. Now using smart pointers. Simply set a variable to nullptr to delete an object. There is no Release() or AddRef() function. Exclusively 64-bit! Global states are gone. There is no "current" world or context. Instead, the object you want is passed into any function that uses it. We are now using constant integers like WINDOW_TITLEBAR instead of static members like Window::Titlebar. Now using global functions where appropriate (CreateWorld(), etc.). Renderer is being designed to be asynchronous so Context::Sync() is gone. 2D drawing is not implemented at this time. Here's the syntax for a simple program.
    #include "Leadwerks.h" using namespace Leadwerks; int main(int argc, const char *argv[]) { auto window = CreateWindow(L"My Game", 0, 0, 1024, 768, WINDOW_TITLEBAR); auto context = CreateContext(window); auto world = CreateWorld(); auto camera = CreateCamera(world); camera->SetPosition(0, 0, -3); auto light = CreateDirectionalLight(world); light->Turn(45, 35, 0); auto model = CreateBox(world); while (true) { if (window->KeyHit(KEY_ESCAPE) or window->Closed()) return 0; if (window->KeyHit(KEY_SPACE)) model = nullptr; world->Update(); world->Render(context); } } You can get access to the Leadwerks 5 Alpha with a subscription of $4.99 a month. You will also be able to post in the Leadwerks 5 forum and give your feedback and ideas. At this time, only C++ is supported, and it will only build in debug mode.  It is still very early in development, so this is really only intended for enthusiasts who want to play with the very bleeding edge of technology and support the development of Leadwerks 5.
  6. 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; }
  7. 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.
  8. Josh
    This update brings the addition of lightmapping across curved surfaces with smooth groups. The image below is a set of lightmapped CSG brushes, not mesh lighting. You can read a detailed account of our implementation of this feature here.
     

     
    The project manager now includes an "Update" button, so you can easily update your project any time we modify files in the template folders. Although we tested with no problems, it is recommended you back up your project before using this feature. The editor will make a copy of any overwritten files, as an added precaution.
     

     
    You've now got full control over all the features and settings in the Leadwerks editor, through the new Options dialog. This gives you control over various program behaviors and settings. You can toggle grid snapping, control the snap angle, and lots of other stuff.
     

     
    We have made one change to the script system. Our multiple script design worked reasonably well during the development of Darkness Awaits. The flowgraph interactions were clean, but when it came to AI and player interaction, things got messy. For example, when the player pushes a button we perform a raycast or proximity test to get the entity hit. Then we have to go through all the scripts attached to the entity, looking for a relevant script attached to it:

    --Check if GoblinAI component is present if entity.script.GoblinAI~=nil then
     
    --Call the TakeDamage() function
    entity.script.GoblinAI:TakeDamage(10)
     
    end
     
    That works okay in our example, but when we consider other enemies we want to add, suddenly it gets ugly. We have a few choices.
    Add an if statement for every new AI script, checking to see if it is present.
     
    Separate the health value out into its own script.
     
    Loop through all attached scripts looking for a TakeDamage() function.

     
    It should be obvious why option #1 is a bad idea. This would make our code highly interdependent. Encapsulation is one of our goals in game scripting, so we can achieve drag and drop functionality (or as close to that as is reasonably achievable without limiting ourselves).
     
    The second option would solve our immediate problem, but this approach means that every single script variable two scripts access has to be a separate script. The thought of this just shuts my creativity down. I already think it's tedious to have to attach an AI and AnimationManager script to an entity, and can't imagine working with even more pieces.
     
    The third option is the most reasonable, but it greatly impedes our coding freedom. It means every time two entities interact, you would have to do something like this:

    --Check if GoblinAI component is present if entity.components~=nil then
     
    for local k,v in entity.components do
     
    if type(v.TakeDamage)=="function" do
     
    --Call the TakeDamage() function
    v:TakeDamage(10)
    end
    end
    end
     
    If you actually wanted to return a value, you would just have to get the first value and exit the loop:

    local enemyhealth=0  
    --Check if components table is present
    if entity.components~=nil then
     
    --Iterate through all attached components
    for local k,v in entity.components do
     
    if type(v.GetHealth)=="function" do
     
    --Call the GetHealth() function
    enemyhealth = v:GetHealth()
    break
    end
    end
    end
     
    This is a major problem, because it means there is no firm "health" value for an entity. Sure, we could consider it a "HealthManager.health" value but therein lies the issue. Instead of having plug-and-play scripts everyone can share, we have to devise a system of expected script names and variables. This breaks compartmentalization, which is what we were going for in the first place. Both Chris and I realized this approach was fundamentally wrong.
     
    After careful consideration, and based on our experience working on Darkness Awaits, we have restructured the system to work as follows, with a 1:1 entity:script relationship:

    --Check if script is present if entity.script~=nil then
     
    if type(entity.script.TakeDamage)=="function" do
     
    --Call the TakeDamage() function
    entity.script.TakeDamage(10)
    end
    end
     
    You can set an entity's script with the new function:

    Entity::SetScript(const std::string& path)
     
    You can also set and get entity script values right from C++:

    virtual void SetString(const std::string& name, const std::string& s); virtual void SetObject(const std::string& name, Object* o); virtual void SetFloat(const std::string& name, const float f);
     
    And it's easy to call a script function from your C++ code:

    virtual bool CallFunction(const std::string& name, Object* extra=NULL);
     
    The properties dialog is changed slightly, with a consistent script tab that always stays visible. Here you can set the script, and properties will appear below, instead of being spread across a bunch of different tabs:

     
    Maps with entities using only one script (which has been almost everything we see) are unaffected. Objects with multiple scripts need to have their code combined into one, or split across multiple entities. I am very reluctant to make changes to the way our system works. Our API has been very stable since day one of release, and I know it's important for people to have a solid foundation to build on. However, I also knew we made a design mistake, and it was better to correct it sooner rather than later.
     
    Probably the best aspect of the script system in Leadwerks 3 has been the flowgraph connections between scripted objects. That's been a big winner:
     

     
    As we use the editor more and hear about the community's experience, it allows us to refine our tools more. One of the more subtle but we made is in the properties editor. One of the annoyances I experienced was when setting a property like mass or position when an entire hierarchy of entities was selected. Obviously I didn't want to set the mass for every single bone in a character, but how to tell the editor that? I went through all the properties, and for ones that the user is unlikely to want set, I made the following rule: If the entity has a selected parent anywhere in the hierarchy, it gets ignored when properties are retrieved and applied. (Other things like color will still work uniformily.) Just try it, and you'll see. It speeds up editing dramatically.
     
    In a similar vein, we finally solved the problem of "too many selection widgets", We only display the top-most control widget for each group of selected objects. Again, it's easier for you to just try it and see than for me to explain in detail. If we did our job right, you might not even notice because the editor will just do what you want without any thought.
     
    You've also got control over the color scheme, and can use it to customize the look and feel of the 3D viewports and code editor.
     

     
    On Windows we fixed an annoyance that triggered menu shortcuts when the user tried to copy and paste in text fields. This tended to happen in the properties editor. We're working to resolve the same issue in the Cocoa UI for Mac.
     
    I'm a big fan of the 3ds max Max Script system, so in the output panel we've added a real-time Lua console:

     
    You can type Lua commands in and interact with Leadwerks in real-time. Just for fun, try pasting in this line of code and see what happens:

    for i=0,10 do a = Model:Box(); a:SetColor(math.random(0,1),math.random(0,1),math.random(0,1)); a:SetPosition(i*2,0,0); end  
    You can't create objects the editor will recognize (yet), but it's fun to play with and should give you an idea of where we're going with the editor.
     
    This is an important update that includes new features and enhancements that improve usability and workflow. Thank you for your feedback. It's great to see all the activity that is taking place as people learn Leadwerks 3.
  9. 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.

     

  10. Josh
    Back when Leadwerks 2 was first launched, it was just a BlitzMax programming SDK, intended to be a modern replacement for Blitz3D. It turned out Blitz users generally did not want it, and instead the audience it attracted was C++ programmers who like graphics. A visual editor was introduced and it was made as good as possible for a product that started with the intention of just being a programming SDK.
     
    Leadwerks 3.0 was launched with a strong emphasis on the tools that Leadwerks 2 lacked, and mobile. It was pretty clear that the main target was mobile. Once again though, the intended audience and the audience that came to me were two different things. There's still a lot of C++ programmers on Windows, but the appeal has broadened to include a lot of modders, artists, and beginners who want an easy to use editor, and of course Linux users. My favorite aspect of Leadwerks 3 is that it serves both advanced programmers and total beginners, with no compromises in the design. (Big win!)

    Leadwerks for Mobile
    My mobile offering, on the other hand, has not done well. There are two problems: 
    I estimate that mobile has accounted for around 80% of the time I spent debugging things in Leadwerks 3.0, yet it accounts for less than 10% of revenue. (It wasn't 80% of the problems, but because the whole process of developing on mobile is painfully slow, it takes more time to do simple things.) Sending an application from the computer to a device can take several minutes, the debuggers are slow, and the whole process is much less transparent than PC development. It's not terribly challenging in an intellectual sense, but it eats a lot of time. For example, it took about two weeks to implement our terrain system, but it took an extra week after that just to get it working on iOS and Android. That week could have been spent adding a different feature, but instead I was messing around with various tablets just trying to make them work.
     
    The other problem is that there's a big disparity between mobile and PC graphics. I saw a huge upswing in interest when I started showing shots of the OpenGL 4.0 renderer. Although we have moved beyond just being a graphics engine, and I know the renderer is a big part of the appeal of Leadwerks. On mobile, the hardware is comparatively limited, so it's much harder for me to do anything that makes Leadwerks mobile stand out.
     
    If I could just hire one engineer dedicated to mobile support, the first problem would be solved, because it wouldn't cut into my time. However, mobile has accounted for less than 10% of revenue in the last year. I didn't even break even on my development costs. PC sales have been consistently strong, but mobile doesn't even make enough to pay for its own maintenance. So what's been happening is the PC side of the business is subsidizing the mobile side.
     
    I expect the second problem will be solved within a couple of years. Nvidia's Tegra 4 chips can supposedly run regular OpenGL 4 (not ES). When those become the norm, it could give us total convergence of graphics between the PC and mobile. At that point, I might actually have a mobile product that stands out and provides something different.

    Leadwerks Tomorrow
    Now, as I am shipping the Kickstarter rewards and about to launch 3.1 proper, I have to think about where I want Leadwerks to be in 6 months, and in 12 months. There are three main areas I want to move forward in: 
    Leadwerks Editor
    Adding new tools to make life easier.
    Refining the workflow to eliminate any remaining"sticky" points.
    Bug fixes, to make it a super slick and polished experience.

     
    The Engine
    New features in graphics, physics, networking, etc.
    Performance optimization.
    Improved compatibility across all OpenGL 4 hardware.

     
    Third-Party Technologies
    Blender, SteamOS, Steam Workshop, virtual reality, etc.

     
    Leadwerks on the PC is in a very good position today. Virtually every computer being sold today will run Leadwerks. We came into desktop Linux at just the right time, and I am very optimistic about that space. SteamOS is opening up the living room for the first time to us lowly indies. Think about where we are now, and where this community can be a year from now...there's a ton of opportunity for all of us, but I need to focus on where we're winning. If I continue to subsidize our mobile efforts, it's going to slow down development and result in a buggier product with fewer features. So I think it's pretty clear mobile should be discontinued as of version 3.1, in favor of a stronger PC-centric product.
     
    It sucks because I lost a lot of money on mobile, and the people most willing to take a chance on a new venture also lost out. But at the same time, if I had just stayed still on Windows and continued along the Leadwerks 2 path, none of the great things we have going for us now would have happened. It may make sense to pick mobile up again, when the hardware can provide enough power, and when there is more interest from the community. So far the response has been pretty much what I expected; a handful of (understandably) disappointed people, but most of our community was never really into mobile, and the new growth is coming from other parts of the PC ecosystem, not mobile.
     
    Support for the OUYA was added rather thoughtlessly as a stretch goal for the Kickstarter campaign. Given the OUYA's failure, it should obviously be scrapped and replaced with SteamOS support. Since Android was a part of that, I am giving Kickstarter backers the ability to opt out of the campaign instead of receiving the final rewards. Kickstarter was an overall good experience, but I probably won't ever do another one, unless the project is really well-defined and doesn't have a lot of unknown parts.
     
    Desktop Linux, VR, and SteamOS are on the upswing, and all of these play to the strengths of Leadwerks. If 2013 was the year of finding our place, I think 2014 is going to be a year of reaching out to the gaming world and making some really astonishing developments.
  11. Josh
    I wanted to add some default procedural generation tools in the Leadwerks 3.1 terrain editor. The goal is to let the user input a few parameters to control the appearance of their terrain and auto-generate a landscape that looks good without requiring a lot of touch-up work.
    Programmers commonly rely on two methods for terrain heightmap generation, Perlin noise and fractal noise. Perlin noise produces a soft rolling appearance. The problem is that Perlin noise heightmaps look nothing like real-life terrain:

     
    Fractal noise provides a better appearance, but it still looks "stylized" instead of realistic:

     
    To get realistic procedural terrains, a more complex algorithm was needed. After a few days of experimentation, I found the optimal sequence of filters to combine to get realistic results.
    We start with a Voronoi diagram. The math here is tricky, but we end up with a grid of geometric primitives that meet at the edges. This gives is large rough features and ridge lines that look approximately like real mountains:

     
    Of course, real mountains do not have perfectly straight edges. A perturbation filter is added to make the edges a little bit "wavy", like an underwater effect. It gets rid of the perfectly straight edges without losing the defining features of the height map:

     
    The next step is to add some low-frequency Perlin noise. This gives the entire landscape some large hills that add variation to the height, instead of just having a field of perfectly shaped mountains. The mixture of this filter can be used to control how hilly or mountainous the terrain appears:

     
    We next blend in some Fractal noise, to roughen the landscape up a bit and add some high frequency details:

     
    Finally, we use thermal and hydraulic erosion to add realistic weathering of our terrain. Thermal erosion works by reducing the harshness of steep cliffs, and letting material fall down and settle. Hydraulic erosion simulates thousands of raindrops falling on the landscape and carrying material away. This gives beautiful rivulets that appear as finger-life projections in the height map: Rather than relying on conventional hydraulic erosion algorithms, I created my own technique designed specifically to bring out the appearance of those features.

     
    Here is an animation of the entire process:

     
    And in the renderer, the results look like the image below. All the parameters can be adjusted to vary the appearance, and then you can go in with the manual tools and sculpt the terrain as desired.

     
    The new landscape has ridges, mountains, and realistic erosion. Compare this to the Perlin and fractal landscapes at the top of this article. It's also interesting that the right combination of roughness and sharp features gives a much better appearance to the texture blending algorithm.
  12. Josh
    Leadwerks uses a left-handed coordinate system. This means that if you hold your left hand as shown below, your middle finger, index finger, and thumb will point in the directions of the X, Y, and Z axes, respectively.
     

     
    Rotations use the left-handed rotation rule. To predict the direction of positive rotation, curl your fingers on your left hand and point your thumb in the direction of the rotational axis. Your fingers will curl in the direction of positive rotation along that axis.
     

     
    This will help you remember how your rotation values affect objects in 3D space.
  13. Josh
    I've got a couple days sales data from the 3.4 launch and it went well. Our conversion ratio and all that boring businessey stuff is good, and I know more now than I did last week. I think our intro video could use some updating and a little more professional polish, so I am looking for a video production company to create something new.
     
    I was planning to make a trip back to Caifornia soon. I was going to skip the GDC, but I then got invited to the Valve party. I guess they're hiring the band Glitch Mob to play at Folsom Nightclub, which is kind of awesome.


     
    This isn't enough to make me want to go, but I remember my Valve VR demo came about because I was randomly talking to someone at Steam Dev Days and they emailed me about it later. So even if you don't get something out of it every time, I think it's necessary that I do these kind of networking things. Not sure if I'll attend the GDC expo itself or not yet, I need to figure out my plans today.
     
    When I get back, work will begin on version 3.5, due out this summer. I want this update to include carving, vegetation, vertex editing, and I have a few other features in mind that will be included based on how much time I have.
     
    There's also a class of improvements I want to make that are more "systemic". This includes driver bugs from third-party vendors (looking at you AMD), bugs or bad design in third-party libraries (GTK), and some improvements I can make in our core renderer design. Uniform buffers and FBOs are two low-level systems I am looking at. I think this will solve the Linux model editor issue and improve performance quite a lot on Linux systems in particular, and have some gains on Windows.
     
    These are potentially destabilizing changes I do not want to attempt this week. I need to be able to do a quick turnaround if any small bugs are discovered right now, and having the renderer all taken apart isn't a good idea at the moment.
     
    Finally, we have a problem with tutorials that needs to be addressed. I keep seeing post after post of people asking where the tutorials are. When you see threads like this, it indicates a problem. I have tried to make the documentation and tutorials pages as "in your face" as possible, yet people still ask where they are...it's the very first thing you see when you run the program! This is probably the most important problem to solve going forward, and has a much stronger effect on us than any feature I can implement or improvement I can make to the software itself.
  14. 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.
  15. Josh
    Physics simulations typically require physical geometry to be provided in a series of convex hulls that approximate the shape of the object. This is because the intersection of two convex objects can be calculated fast enough for use in real-time simulations.
     
    One solution for creating this type of physical geometry is to create a low-detail model of the object in a modeling program. However, this process can be tedious and is often overlooked by artists. Auto-generating this type of geometry from the visual mesh itself would be ideal.
     
    Convex decomposition is a technique that takes an artbitrary polygonal mesh and transforms it into a series of convex hulls. Unfortunately until recently such algorithms were unreliable and produced physics geometry that was far too high-detail for real-time physics, as shown in the image below:
     

     
    One solution was to model a low-poly polygonal mesh and then run that through the convex decomposition routine. Although this relieved the artist from the requirement of modeling shapes as separate convex objects, it created a situation that was the worst of both worlds; physics geometry still had to be modeled by hand, but the unreliability of a convex decomposition algorithm also had to be accounted for. For this reason, I have chosen to avoid using this technique until now. However, physics geometry is our bottleneck right now in getting more content into Leadwerks, and this is a problem that has been on my mind.
     
    VHACD is a new(er) convex decomposition library. Apparently it's been around for a while, but I've only heard of HACD until recently, which did not produce very usable results. VHACD works by converting polygonal geometry into voxel data and creating a series of convex shapes from this data. Integrating this into Leadwerks took just two days, as it was very similar to the old HACD code, which was integrated at one point, but never saw the light of day because I did not feel it was good enough to release. The results of the new algorithm are, in most cases, about as good as hand-modeled geometry. This is generated purely from the visual geometry, so there is no need to hand-model a low detail collision mesh:
     

     

     

     

     

     
    You can try this feature out right now if you opt in to the beta branch on Steam, on Windows. Open a model in the editor and select the Tools > Convex Decomposition... menu item to open the convex decomposition dialog.
     
    Thanks to Khaled Mamou and Julio Jerez for their work in this area.
  16. Josh
    I'm a big fan of constructive solid geometry because it allows people without advanced modeling skills to design a game level that looks great. In fact, I originally got my start in game development using the Quake and Half-Life modding tools, making game maps.
     
    One of the criticisms of CSG has been that it only allowed creation of faceted objects. (Valve's Hammer Editor has a workaround for this that lets you set smooth groups, but you can't see the results until they are run in the game.) This was preventing me from making some game level features I wanted, like curved arches and rounded corners, so I decided to do something about it.
     
    Leadwerks supports smooth groups for CSG objects. To access the controls, switch to face editing mode and then select the objects tab in the right-hand sidepanel:

     
    It was fairly easy to calculate vertex normals from smooth group information. However, that information still has to be fed to the lightmapper or the lighting will appear faceted across a curved surface:

     
    To solve this, I needed to calculate the interpolated normal across the surface, and use that for the lighting equation for each luxel (lightmap pixel). Initially, I thought I could use a simple weighted average. Vertices near the luxel would have a high influence, and vertices further away would have less influence. However, it quickly became apparent this would not produce anything close to accurate results!

    Gouraud Shading
    The problem I was facing is actually a very common calculation that is done in real-time on the GPU. This was the first time I ever had to calculate it myself. It turns out the problem was first solved before I was born by a clever fellow by the last name of Gouraud, and thus we call it Gouraud Shading or Gouraud Interpolation. 
    The algorithm works like this: draw a straight line in any direction from the point you want to interpolate. It doesn't matter what angle, as long as it's one straight line. Now find the two triangle edges the line intersects. Each of those edges is connected to two vertices, each with a normal. Use a linear interpolation to weight those two normals, for each point. Finally, use the distance of both these points from your original position to weight their interpolated normals:

    More information on this technique can be found here.

    Implementation
    Getting this to work on a CSG lightmapper was difficult for two reasons. First, CSG objects consist of n-sided polygons, not triangles. Although they can be broken down into triangles, I was worried that visual artifacts might arise. Second, lightmaps have a user-defined bleed border, and the luxels of a lightmap extend beyond the edges of the polygon being lit. Gauroud shading requires the point being interpolated actually be inside the triangle. Our luxel positions could be inside any of the triangles that make up a polygon face, or they might not be on the face at all! 
    I decided to start by only worrying about the luxels that fell inside one or another triangles on the face, and solve the outliers later. Fortunately, the transform and math classes built into Leadwerks 3 made it fairly easy to convert all the points into flat 2D space to solve the problem. As expected, my first attempt identified the luxels that fit inside a particular triangle, but the luxels along the edges could not be processed, and appear dark:

     
    I added an error threshold for the triangle intersection routine, which got rid of the black borders, but turned out to be a bad idea. Some of my values were being interpolated in the wrong direction, as you can see in the following images:

     

     

     
    In this image, it's almost working, but the error threshold is causing luxels along the center seam to get lit incorrectly. Additionally, a few luxels in the top right are forming a dark border:

     
    The final piece of this puzzle was to deal with luxels that didn't fit into a particular triangle, This was a pretty puzzling problem, and for a while I thought there might not be a "correct" solution. However, if you think about it intuitively, a luxel that lies just outside a triangle should use the same lighting as a luxel just inside that triangle, right next to it.
     
    For the remaining unsolved luxels, I tested each of their distances to each triangle in the face they belong to. I found a nearest triangle to each, then found the nearest point on that triangle, and calculated the normal from that point's position.

    Results
    This technique produces beautiful smooth lightmapping on curved surfaces:
     
    The algorithm works with curves, arches, sphere, any CSG objects that use smooth groups. So now you can make those castles, arches, and towers you've always wanted to build:

     
    This feature will be available in the next update to Leadwerks 3.
  17. Josh
    The Vulkan renderer now supports new texture compression formats that can be loaded from DDS files. I've updated the DDS loader to support newer versions of the format with new features.
    BC5 is a format ATI invented (originally called ATI2 or 3Dc) which is a two-channel compressed format specifically designed for storing normal maps. This gives you better quality normals than what DXT compression (even with the DXT5n swizzle hack) can provide.
    BC7 is interesting because it uses the same size as DXT5 images but provides much higher quality results. The compression algorithm is also very long, sometimes taking ten minutes to compress a single texture!  Intel claims to have a fast-ish compressor for it but I have not tried it yet. Protip: You can open DDS files in newer versions of Visual Studio and select the compression format there.
    Here is a grayscale gradient showing uncompressed, DXT5, BC7 UNORM, and BC7 SNORM formats. You can see BC7 UNORM and SNORM have much less artifacts than DXT, but is not quite the same as the original image.





    The original image is 256 x 256, giving the following file sizes:
    Uncompressed: 341 KB DXT1: 42.8 KB (12.6% compression) DXT5, BC7: 85.5 KB (25% compression) I was curious what would happen if I zipped up some of the files, although this is only a minor concern. I guess that BC7 would not work with ZIP compression as well, since it is a more complicated algorithm.
    DXT5: 16.6 KB BC7 UNORM: 34 KB BC7 SNORM: 42.4 KB Based on the results above, I would probably still use uncompressed images for skyboxes and gradients, but anything else can benefit from this format. DXT compression looks like a blocky green mess by comparison.
    I was curious to see how much of a difference the BC5 format made for normal maps so I made some similar renders for normals. You can see below that the benefits for normal maps are even more extreme. The BC5 compressed image is indistinguishable from the original while the DXT5n image has clear artifacts.



    In conclusion, these new formats in the Vulkan renderer, when used properly, will provide compression without visible artifacts.
  18. 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/
  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
    I'm using the excellent sol2 library to interface C++ and Lua in the upcoming Turbo Game Engine. I've decided not to create an automatic header parser like I did for tolua++ in Leadwerks 4, for the following reasons:
    There are a lot of different options and special cases that would probably make a header parser a very involved task with me continually discovering new cases I have to account for. sol2 is really easy to use. Each class I want available to Lua will have a static function the Lua virtual machine code can call when a new Lua state is created. The new_usertype method is able to expose a C++ class to Lua in a single command. At a minimum, the name of the class and the base class should be defined. This method can accept a lot of arguments, so I am going to break it up over several lines.
    void Vec3::InitializeClass(sol::state* luastate) { //Class luastate->new_usertype<Vec3> ( //Name "Vec3", //Hierarchy sol::base_classes, sol::bases<Object>() ); } We can export members to Lua very easily just by adding more arguments in the call to new_usertype:
    //Members "x", &Vec3::x, "y", &Vec3::y, "z", &Vec3::z, Metamethods are special operations like math operands. For example, adding these arguments into the method call will set all the metamethods we want to use.
    //Metamethods sol::meta_function::to_string, &Vec3::ToString, sol::meta_function::index, [](Vec3& v, const int index) { if (index < 0 or index > 2) return 0.0f; return v[index]; }, sol::meta_function::new_index, [](Vec3& v, const int index, double x) { if (index < 0 or index > 2) return; v[index] = x; }, sol::meta_function::equal_to, &Vec3::operator==, sol::meta_function::less_than, &Vec3::operator<, sol::meta_function::subtraction, sol::resolve<Vec3(const Vec3&)>(&Vec3::operator-), sol::meta_function::addition, sol::resolve<Vec3(const Vec3&)>(&Vec3::operator+), sol::meta_function::division, sol::resolve<Vec3(const Vec3&)>(&Vec3::operator/), sol::meta_function::multiplication, sol::resolve<Vec3(const Vec3&)>(&Vec3::operator*), sol::meta_function::unary_minus, sol::resolve<Vec3()>(&Vec3::operator-), sol::meta_function::modulus, &Vec3::operator%, And then finally the class methods we want to use can be exposed as follows:
    //Methods "Length", &Vec3::Length, "Cross", &Vec3::Cross, "Normalize", &Vec3::Normalize, "Inverse", &Vec3::Inverse, "Distance", &Vec3::DistanceToPoint In C++ you can not retrieve a pointer to a function, so we are going to create a quick Lambda expression and expose it as follows:
    //Constructor luastate->set_function("Vec3", [](float x, float y, float z) {return Vec3(x, y, z); } ); This allows us to create a Vec3 object in Lua the same way we would with a constructor in C++.
    Here is the complete Vec3 class initialization code, which makes Lua recognize the class, exposes the members, adds math operations, and exposes class methods:
    void Vec3::InitializeClass(sol::state* luastate) { //Class luastate->new_usertype<Vec3> ( //Name "Vec3", //Hierarchy sol::base_classes, sol::bases<Object>(), //Members "x", &Vec3::x, "y", &Vec3::y, "z", &Vec3::z, "r", &Vec3::x, "g", &Vec3::y, "b", &Vec3::z, //Metamethods sol::meta_function::to_string, &Vec3::ToString, sol::meta_function::index, [](Vec3& v, const int index) { if (index < 0 or index > 2) return 0.0f; return v[index]; }, sol::meta_function::new_index, [](Vec3& v, const int index, double x) { if (index < 0 or index > 2) return; v[index] = x; }, sol::meta_function::equal_to, &Vec3::operator==, sol::meta_function::less_than, &Vec3::operator<, sol::meta_function::subtraction, sol::resolve<Vec3(const Vec3&)>(&Vec3::operator-), sol::meta_function::addition, sol::resolve<Vec3(const Vec3&)>(&Vec3::operator+), sol::meta_function::division, sol::resolve<Vec3(const Vec3&)>(&Vec3::operator/), sol::meta_function::multiplication, sol::resolve<Vec3(const Vec3&)>(&Vec3::operator*), sol::meta_function::unary_minus, sol::resolve<Vec3()>(&Vec3::operator-), sol::meta_function::modulus, &Vec3::operator%, //Methods "Length", &Vec3::Length, "Cross", &Vec3::Cross, "Normalize", &Vec3::Normalize, "Inverse", &Vec3::Inverse, "Distance", &Vec3::DistanceToPoint ); //Constructor luastate->set_function("Vec3", [](float x, float y, float z) {return Vec3(x, y, z); } ); } To add your own C++ classes to Lua in Turbo, you will create a similar function as above and call it at the start of the program.
  21. Josh
    Leadwerks3D will ship with a finished game demo to demonstrate how to use the software. Darkness Awaits is a third-person dungeon explorer with a 45 degree view. It's like a cross between Diablo and Legend of Zelda: A Link to the Past. This is an idea I had back in my very early days of game development, before I even learned to program. This was originally done in the Quake 1 engine. It didn't really go anywhere, but what we had was awesome. You could run around and shoot skeletons with flaming arrows, in a third-person view. B) My job was actually map design. My favorite part was this cozy little house you started off in with a fireplace and a warm feeling, before going outside into a snow storm to kill monsters.
     
    And so, the project is being resurrected to demonstrate game development with Leadwerks3D. Below are a few possible logo styles. Which is your favorite?
     




  22. Josh
    I experienced some problems this week when I tried to create an executable with Visual Studio 2008 for deployment. On Windows 7 test machines I would get this error:
     
    I finally tracked the solution down to a Visual Studio project setting. In Project Settings > Configuration Properties > C/C++ > Code Generation there is a property called "Runtime Library". By default it is set to rely on external DLLs. Change these values to non-DLL settings (like MT or MTd) and they will include the runtime library in the executable. This makes for bigger executables, but they will run everywhere.

     
    Having experienced this problem with Visual Studio 2010, I guessed this was the same issue. I uninstalled the Visual C++ 2010 Redistributable Package from my Windows XP machine, then created a simple test program on another Windows 7 machine. First I made sure I could create the problem, by using the default runtime library. Ever seen this error?:

     
    I've seen this issue come up before on this forum. Now you know how to solve it and build Debug or Release executables that will run everywhere, with Visual Studio 2008 and 2010. Here is my test application from Visual Studio 2010, "testapp.MDd.bat" will only run if you have the 2010 Redistributable Package installed. "testapp.MTd.bat" will run everywhere:
    testapp.zip
     
    Since we no longer have problems creating executables for Windows XP with Visual Studio 2010, I see no reason not to move our project on to a newer version. Visual Studio 2012 currently cannot make Windows XP executables, but Microsoft claims this will be supported soon. However, even if deployment to Windows XP is supported, Visual Studio 2012 itself will never run on Windows XP. Switching to Visual Studio 2012 would mean you cannot program Leadwerks with C++ on Windows XP.
     
    What do you think? Should we make the move to 2012, 2010, or stick with 2008?
  23. Josh
    Last week I compiled the engine into a DLL and a static lib. That was pretty painless, and I have the project set up so it's easy to switch back and forth. I still haven't got Code::Blocks compiling right, which I will need for the Linux build. It can't seem to find any libs, but it's not too important yet.
     
    After altering the behavior of the MaxGUI canvas slightly, I was able to load the engine DLL in BlitzMax and display 3D rendering on a windowed application. It's very easy to do this in LE3:

    gadget:Byte Ptr = CreateWindowsCustomGadget( QueryGadget(canvas,QUERY_HWND) ) context:Byte Ptr = CreateContext( gadget )
    Or with C:

    Gadget* gadget = CreateWindowsCustomGadget( HWND hwnd ); Context* context = CreateContext( gadget );
    The editor is taking shape, though it is still very early. It feels like a cross between Visual Studio and Valve Hammer Editor (Worldcraft) which are both programs I like a lot.

    I knew from the beginning the only acceptable way to display assets would be a draggable, multi-select treeview with icons. For the scene hierarchy, I wanted checkboxes and icons. I looked at ways to hack the Windows treeviews and I looked at wxWidgets, and didn't find anything suitable that would work on all platforms. At last resort, I started writing my own custom control on a graphics window, and I'm getting surprisingly good results with that. The behavior is about 60% functional now, and it's faster than the Windows tree view because it's completely hardware accelerated. The only problem is that the FreeType text does not match the Windows GUI font 100% completely, even though it's using the same font and size. I'll see if there's anything I can do to make it match more closely.
     
    The reason I am spending so much time on this tree control is because drag and drop is used everywhere in the new editor. You can drag a texture onto a material, drag a material onto a model, rearrange nodes in the scene hierarchy, and even move files around on your hard drive. So it's not surprising if some customization is needed to support this functionality.
     
    Knowing the kinds of things people want to do like copy all used assets into a folder ("Publish") and import a bunch of models they downloaded off the internets, it's kind of fun to design interfaces to make development easier. My goal for the LE3 editor is to have more of an established official procedure for everything, along with extensive documentation. I've gotten lots of feedback from some fantastic artists, so I know what will help them get a large project done faster.
     
    That's all for now. I've got a lot to do!
  24. Josh
    Frictional Games is the developer of puzzle horror games like Penumbra and the new Amnesia: The Dark Descent. We sat down with Frictional Games co-founder Thomas Grip to learn more about their development process.
     
    Josh: I'm a big fan of the Penumbra series. What can we look forward to in Amnesia: The Dark Descent?
     
    Thomas Grip: There is a lot to of new stuff in the game. First of all the game is a more streamlined experience both in terms of general gameplay and interaction. For the gameplay we have made sure that the focus is always on atmosphere and story, and made sure that the player is never stuck at a puzzle for too long and stuff like that. One of the ways we do this is to allow multiple solutions so it always feels intuitive to the player.
     
    In terms of interaction, it is much easier to interact now. In Penumbra it could be a pain to open doors at times because you needed to click on the right spot and so on. In Amnesia all that is fixed and it is now possible to easily slam shut a door in the face of an enemy and things like that.
     
    There is also a new insanity system, a fresh environment, and new story.
     



    Josh: Can you talk about the process of developing an idea and turning it into a full-fledged game? Do you start with a design document or just start writing code? How closely does your final game resemble what you originally imagined?
     
    Thomas Grip: This time we had a very long iteration period where we tested out various ideas. We actually first had a bite-sized horror design inspired by Super Mario. It was intended to have small self-contained levels and to be played in shorter bursts. It did not fall out too well and we tried tons of stuff before settling where the final game is.
     
    In the early iterations we started out with a "fun" mechanic and tried to base a game on that, but as I said, it did not turn out good. Instead we started focusing on the themes and general feel of the game. Then we started building minimal mechanics to support that. To do this we took a lot from Penumbra, as we knew how it worked, and refined it.
     
    Once we had the general feel going 1/3 or so is drawn up on paper and we started making maps based on that, then as maps are made we continue to design the remaining 2/3 on paper taking cues from the maps being built. These written paper designs are often very close to the full game and that is a way for us to be able to plan ahead very accurately. Early on we knew around 90% of the assets that we needed, which is great when outsourcing. Then in the final steps the maps get polished and we play them over and over and try to build up the feel we are after. If something does not work out, we often scrap it instead of rebuilding it.
     



    Josh: Towards the end of the first Penumbra game, you communicated with a major character over an intercom system. The character never actually appeared on screen, but the emotional connection was established using voice acting. This struck me as a good design decision, because you were able to eliminate all the trouble of making an animated character speak in a convincing cut scene, without detracting from the player's experience. Are there any other examples like that where you made design decisions that helped streamline production, so you could focus on the core gameplay?
     
    Thomas Grip: There is a lot of that stuff in Amnesia. For example, there is one character without a lower jaw, which removed the need to do lip-sync. When it comes to visions we only use sound (without models, like in System Shock 2), which in turn also gives the game a more spooky feel. I think having these kinds of constraints also forces you to use your imagination more and not just follow the design of other games. Although it is a bit annoying not being able to do certain stuff, it often turns out positive for us.
     



    Josh: What's next for Frictional Games?
     
    Thomas Grip: Rest! And tech support... And then we have to see how sales and so go! We have an interesting idea we would like to pursue, but it depends a bit on how we do financially.
     
     
    Check out Amnesia: The Dark Descent for creepy thrills and inspiring game design.
×
×
  • Create New...