Jump to content

Josh

Staff
  • Posts

    23,607
  • Joined

  • Last visited

Blog Entries posted by Josh

  1. Josh
    C++11 introduces shared pointers, a powerful language feature that provides easy memory management without the overhead of garbage collection.  The example below should look familiar to you:
    #include "Leadwerks.h" using namespace Leadwerks; int main(int argc, const char *argv[]) { shared_ptr<Leadwerks::Window> window = Leadwerks::Window::Create(); shared_ptr<Context> context = Context::Create(window); shared_ptr<World> world = World::Create(); shared_ptr<Camera> camera = Camera::Create(); camera->SetRotation(35, 0, 0); camera->Move(0, 0, -6); shared_ptr<Light> light = DirectionalLight::Create(); light->SetRotation(35, 35, 0); shared_ptr<Model> model = Model::Box(); model->SetColor(1.0, 0.0, 0.0); model->SetPosition(-4, 0, 0); while (true) { if (window->Closed() || window->KeyDown(Key::Escape)) return false; Leadwerks::Time::Update(); world->Update(); world->Render(); context->Sync(); } //Everything will get automatically deleted when we return from this function return 0; } Using the auto keyword simplifies everything (and it makes this code compatible with Leadwerks 4):
    #include "Leadwerks.h" using namespace Leadwerks; int main(int argc, const char *argv[]) { auto window = Leadwerks::Window::Create(); auto context = Context::Create(window); auto world = World::Create(); auto camera = Camera::Create(); camera->SetRotation(35, 0, 0); camera->Move(0, 0, -6); auto light = DirectionalLight::Create(); light->SetRotation(35, 35, 0); auto model = Model::Box(); model->SetColor(1.0, 0.0, 0.0); model->SetPosition(-4, 0, 0); while (true) { if (window->Closed() || window->KeyDown(Key::Escape)) return false; Leadwerks::Time::Update(); world->Update(); world->Render(); context->Sync(); } //Everything will get automatically deleted when we return from this function return 0; } Now things get interesting.  This function would normally cause a horrible memory leak, but with shared pointers everything is fine:
    void SaveTexture(shared_ptr<Texture> tex) { auto bank = Bank::Create(tex->GetMipmapSize()); tex->GetPixels(bank->buf); bank->Save("pixeldata.dat"); } Yet shared pointers can still equal nullptr:
    auto bank = Bank::Create(); bank.reset(); Debug::Assert(bank==nullptr); You can even simply set a shared pointer to nullptr, and if that was the last pointer that referenced it, it gets deleted!
    auto bank = Bank::Create(); bank = nullptr;// auto deletion here! How to Delete an Entity
    The code below will not delete the entity, because a shared pointer is still stored in the world.
    auto entity = Pivot::Create(); entity = nullptr; The entity must be have its world set to NULL, and the shared pointer must be set to NULL or go out of scope:
    entity = Pivot::Create(); entity->SetWorld(nullptr); entity = nullptr; Children use a weak pointer to the parent, so they will not cause self-referencing.
    Asset Management
    You no longer have to worry about calling Release() when loading assets:
    auto material = Material::Create(); auto texture = Texture::Load("mytex.tex"); material->SetTexture(texture); texture = nullptr; Unused assets will automatically be deleted if they go out of scope:
    auto material = Material::Create(); auto texture = Texture::Load("mytex.tex"); material->SetTexture(texture); texture = nullptr; material = nullptr; But if they are in use, they will be retained in memory:
    auto material = Material::Create(); auto texture = Texture::Load("mytex.tex"); material->SetTexture(texture); model->SetMaterial(material); texture = nullptr; material = nullptr; In conclusion, shared pointers automate many of the tasks we have been doing manually with the Leadwerks reference counting system and the AddRef and Release commands.
  2. Josh
    Distance fog is one of the most basic visual effects in 3D graphics, going back to the 1990s.  Here is the effect in the Quake 3 Arena map "Fatal Instinct", which was shrouded in a dense orange fog:

    Leadwerks Game Engine 2 had this available as a built-in effect, while the more flexible effects system of Leadwerks 3/4 had several Workshop shaders available to use, including one by Klepto and another one more recently added by myself.  However, this has not been part of the official SDK until version 4.4.  Why is that?
    The Problem
    When water is rendered in Leadwerks, a low-quality render is performed of the world with the camera scale inverted on the Y axis.  Because the reflection is distorted by ripples, we render to a lower-resolution buffer with all settings on low and effects disabled.  This is important because it gives a faster performance and image quality that is still acceptable.  The water plane also uses occlusion culling so that the extra pass is only rendered if part of the water plane is visible.  All post-processing effects are disabled in the reflection pass, which makes good sense, except in the case of fog.  If the world is shrouded in dense fog but the reflection in the water is clear, it creates an obvious problem.  In the screenshot below, a post-processing effect is applied to the world.  Although the water does have fog applied to it, the reflected image on the water does not have any fog, creating a stark problem, because post-processing effects are disabled in the reflection pass.

    Pre-Rendering Fog
    One option would have been to allow the user to mark some post effects as visible in reflection passes, but that seemed complicated and error-prone.  I came up with the idea build a simple fog calculation into the first ambient or directional lighting pass.  Here it is applied in the directional light pass:

    And here is what happens when the fog effect is applied in the directional light pass in the water reflection as well:

    We can modify the water shader itself to add the same fog calculation to the surface of the water.  Now our water matches the world.

    Removing Artifacts
    There was still more to do.  We are rendering fog first and other stuff later.  Anything rendered after the main lighting pass has to use the fog calculation to substract its effect from the scene.  For example SSAO will add shaded areas on top of fog, which we definitely don't want.  See the bridge and trees in the distance.

    The solution is to add the same fog calculation into the SSAO shader:
    float fogeffect = 0.0f; if (fogmode==true) { fogeffect = clamp( 1.0 - (fogrange.y - length(worldCoord - cameramatrix[3].xyz)) / (fogrange.y - fogrange.x) , 0.0, 1.0 ); fogeffect*=fogcolor.a; if (fogeffect==1.0f) { fragData0 = outputcolor; return; } } And then use the fog level to lessen the impact of the effect:
    fragData0 = outputcolor * fogeffect + outputcolor * (sumao / float(passes)) * (1.0 - fogeffect); The underwater artifacts are a separate issue that were solved by adding another calculation.  Here is the result:

    The same fog calculation had to be added to all light shaders, light volumes, and probes, to make sure lights faded into the fog.  Other effects can make use of four new built-in uniforms which will provide all the fog information the shader needs:
    uniform bool fogmode; uniform vec2 fogrange; uniform vec4 fogcolor; uniform vec2 fogangle; On the client side, eight new commands have been added to the camera class to control the fog appearance, which are also available in Lua:
    virtual void SetFogColor(const float r, const float g, const float b, const float a); virtual void SetFogAngle(const float start, const float stop); virtual void SetFogRange(const float start, const float stop); virtual void SetFogMode(const bool mode); virtual Vec4 GetFogColor(); virtual Vec2 GetFogAngle(); virtual Vec2 GetFogRange(); virtual bool GetFogMode(); This was the solution I've had in mind for some time, but I haven't had a chance to implement it until now.  It works really well and provides robust fog that looks correct under a wide variety of settings.

  3. Josh
    A new build is available on the beta branch.  I am putting it out tonight because I want to give you guys as much lead time as possible.
    I have not tested all hardware yet, and there were a lot of shader updates, so there could easily be a compile error on AMD or Intel cards.  Will test those out in the morning.
    New camera commands for fog:
    virtual void SetFogColor(const float r, const float g, const float b, const float a); virtual void SetFogAngle(const float start, const float stop); virtual void SetFogRange(const float start, const float stop); virtual void SetFogMode(const bool mode); virtual Vec4 GetFogColor(); virtual Vec2 GetFogAngle(); virtual Vec2 GetFogRange(); virtual bool GetFogMode(); This is scheduled to be released on the default branch this Tuesday.
    Also useful:
     
  4. Josh
    Leadwerks Game Engine 4.4, scheduled for release soon, features some updated and enhanced visual effects.  In this blog I will talk about some of the adjustments I made.  Having "The Zone" scene that Aggror recreated actually helped a lot to see how shaders could be improved.
    Bloom / Iris Adjustment / HDR
    The bloom and iris adjustment shaders have been updated to give bloom a wider and softer blur.  Iris adjustment is faster and more intense now, which will make the outdoors areas seem very bright when you are indoors, and the indoors areas will look very dark when you are outdoors.


    SSAO
    The SSAO shader has multiple passes added to it, resulting in a much smoother yet crisp result. 



    Vegetation Shaders
    A new vegetation shader called "groundrocks" will make objects align to the terrain.  This lets you easily paint clusters of rocks all across a landscape, resulting in a nice chunky look that breaks up boring heightmap terrain.

    Built-in Fog
    Although several fog shaders have been available in the Workshop for some time, version 4.4 adds a built-in distance fog you can use to make spooky scenes.

    The fog is built into several shaders in order to give correct reflections.  It works great with water.  Notice the reflection is also foggy, and the water itself is affected by fog, giving a nice misty look to the far side of the lake.

    You can try Leadwerks Game Engine 4.4 right now by opting into the beta branch on Steam.
  5. Josh
    This tutorial demonstrates how to create a high-quality skybox for Leadwerks Game Engine using Vue.
    Download
    Cloudy Blue Skies.zip FixVueCubemap.zip Required Third-Party Programs
    Vue Esprit Exporter Module Loading the Example
    Run Vue and select the File > Open menu item.  Extract the zip file above and open the file "Cloudy Blue Skies.vue".

    Atmosphere and Clouds
    You can modify the appearance of the sky with the Atmosphere Editor. Select the Atmosphere > Atmosphere Editor menu item to open this dialog.

    The clouds tab lets you adjust various properties of the cloud layers and add new ones. Skyboxes look best with multiple layers of different kinds of clouds, so don't expect to get the perfect look with just one layer.

    The load button to the right side of the cloud layer list will let you select from a wide range of different cloud types.

    Experiment with different cloud layers to get the look you want. The "Detail amount" setting in particular will really enhance the image, but don't overdo it. You can right-click and drag the mouse to look around in the main panel, so be sure to take a look around to see how the clouds affect the entire sky.
    Lighting
    To edit the sunlight properties in Vue, select the sunlight object in the World Browser on the right side of the main window.

    You can match the exact rotation of the default sunlight angle in Leadwerks to make your skybox line up exactly to the scene lighting. The default sunlight angle in Leadwerks is (55,-35,0). In Vue this corresponds to the values (145,0,215). To get these values we add 90 degrees to the pitch and subtract the yaw from 180. Note in Vue the order of the yaw and roll are switched.

    The sun color is very important for the overall composition of our image. In real life we're used to seeing a very high range of light levels in the sky. Computer monitors cannot represent the same range of colors, so images can easily become washed out and lose details. We want to adjust the sun color so we can get the most detail within the spectrum of a 32-bit color display. Like the rotation, the sun color can be modified in the sun properties.

    If the sunlight color is too bright, the image will be overexposed and the cloud shape will become washed out.

    If the sunlight is too dark, it will look gray and desaturated.

    The right sun brightness will give a balanced look between bright spots and shadows. This is the part in the process that requires the most artistic sense. It's a good idea to look at some screenshots or photos for comparison as you adjust your settings.

    You will get quite a lot of variation in brightness across the sky, so be sure to take a look around the whole scene when you are adjusting lighting. You can also adjust the main camera's exposure value to vary the brightness of the rendered image.
    If you want to hide the sun from view you can do this by setting the "Size of the sun" and "Size of the corona" settings both to zero under the "Sun" tab in the Atmosphere Editor. Exporting
    To export our skybox the exporter module must be installed. Select the File > Export Sky menu item and the export dialog will appear.

    The "Supporting geometry" setting should be set to "Cube". Set the X value to 1536 and the Y value to 2048. This controls the width and height of the saved image. When we press the OK button, the sky will be rendered out into a vertical cube cross with those dimensions. Each face of the cubemap will be 512x512 pixels.

    By default, your skybox will be exported to the file "Documents\e-on software\Vue 2015\Objects\Atmosphere.bmp". The exported cube cross is a nonstandard orientation. To convert this into a cubemap strip ready to load in Leadwerks, use the FixVueCubemap.exe utility posted above.  Drag your exported image file onto the executable, and it will save out a cube strip in PNG format that is ready to load in Leadwerks.

    Importing
    To import your skybox into Leadwerks, just drag the cubemap strip PNG file onto the Leadwerks main window. Open the converted texture from the Leadwerks Asset Browser. Set the texture mode to "Cubemap", uncheck the "Generate Mipmaps" checkbox, and check the clamp mode for the X, Y, and Z axes. Press the Save button to reconvert the texture and it will appear in a 3D view.

    You can use the skybox in the current map by selecting it in the scene settings.

    Disabling mipmap generation will reduce the size of a 1024x1024 cubemap from 32 to 24 mb. Due to the way the image is displayed, mipmaps aren't needed anyways. Final Render
    For the final render, we want each cubemap face to be 1024x1024 pixels. However, we can get a better quality image if we render at a larger resolution and then downsample the image. In Vue, select the File > Export menu item again to open the export dialog. This time enter 6144 for the X value and 8192 for the Y value. Don't press the OK button until you are ready to take a long break, because the image will take a long time to render. When you're done you will have a huge image file of your skybox with a 2048x2048 area for each cubemap face.
    If we resize the image file in a regular paint program, it will create seams along the edges of the cubemap faces. Instead, we're going to pass a parameter to the conversion utility to tell it to downsample the image by a factor of 50%. The "downsample.bat" file is set up to do this, so just double-click on this to launch the executable with the correct parameters. The resulting cubemap strip will be 6144x1024 pixels, with a 1024x1024 area for each face. However, due to the original rendering resolution this will appear less grainy then if we had rendered directly to this resolution.
    Import this texture into Leadwerks as before and enjoy your finished high-quality skybox. Always do a low-resolution pass before rendering the final image, as it can take a long time to process.
  6. Josh
    A new build is up on the beta branch, for Windows and Linux, with Lua and C++ support.  This is a full update.
    Version 4.4 will be announced June 26th, and this will go on the default branch some time next week.
    I was not able to get the new vehicles working in time, and It will have to be added in an update.  Version 4.4 will have vehicles disabled.  If you need this functionality, stick with version 4.3.
    A big thanks for all the people who are helping to test it.
  7. Josh
    An update is up which saves all menu settings into your game's config file.  When your program calls System:SetProperty() the inputted key-value pair is saved in a list of settings.  Your game automatically saves these settings to a file when it closes, located in C:\Users\<USERNAME>\AppData\local\<GAMENAME>\<GAMENAME>.cfg.
    The contents of the config file will look something like this:
    anisotropicfilter=8 antialias=1 lightquality=1 screenheight=720 screenwidth=1280 session_number=2 terrainquality=1 texturedetail=0 trilinearfilter=1 verticalsync=1 waterquality=1 When your game runs again, these settings will be automatically loaded and applied.  You can override config settings with a command line argument.  However, command lines arguments will not be saved in the config file.
    This has been my plan for a long time, and is the reason why your game is not set to use the editor settings.  Setting for running your game in real-time should be separate from editor settings.
  8. Josh
    Along with Leadwerks GUI, Leadwerks 4.4 adds an in-game menu that is available with the default Lua scripted game.  You can use this to allow your users to adjust settings in the game, or provide a more sophisticated way to quit the game than simply pressing the escape key.

    The default window size has been changed to 1280x720 when run from the editor.  Your game will now run in fullscreen mode by default when it is launched outside the editor.
    All of these changes are contained in the Main.lua and Menu.lua scripts, and can all be modified or removed to your heart's content.
    A full build is available now on the beta branch.
  9. Josh
    In Leadwerks 4.3 we integrated GameAnalytics.com into our software, both in the editor and in the engine, as a tool developers can use to track their player statistics.  A number of events were set up in the editor to fire when certain actions were performed, in order to gain better insight into how people were using Leadwerks.  Here are the results.
    The most popular primitives
    Unsurprisingly, boxes are by far the most popular primitive created in Leadwerks Editor.  The community has created more than 20,000 boxes since analytics were enabled.  What blows my mind is that cylinders are actually the second-most commonly used primitive, rather than wedges.  Users created 1753 cylinders but only 1358 wedges in the given time period!  Even more shocking is that spheres are more popular than cones, with 985 spheres created versus just 472 cone primitives.
    This causes me to question my assumptions of how people use Leadwerks, and how items in the interface should be prioritized.

    The Workshop Store is used heavily
    Leadwerks users installed more than 5000 items from the Workshop.  It also looks like people install many items, as sometimes the number of installs exceed the number of times the Workshop interface is opened.

    People buy Workshop items directly through the Steam Client
    Although users are buying Workshop items in high quantities, it appears that their primary route is through the Steam store interface, rather than the built-in Workshop browser.  People browse the items in the Steam client and then use the Workshop browser to install their purchased items.  The numbers for Workshop Store purchases are much higher than the number of people clicking the buy button in the editor.
    If you're interested in selling your 3D models or textures through the Leadwerks Workshop Store, it's easy to get started and you can earn money directly from Steam.  Contact us to learn more or stop by the forum.
  10. Josh
    Leadwerks Game Engine 4.4 has been updated on the beta branch on Steam.
    Networking finished and documented. GUI finished. All new physics features finished. The character controller physics and picking up objects has been improved and made smoother.  There is a problem with the player sliding down slopes, as seen in the FPS Character Controller example map.  I will work this out.
    I also noticed during testing that picking up some objects in the FPS / AI map will freeze the game.  Will check it out.
    I have not actually tested compiling on Linux yet, because my Linux machine is in the office and I am at home right now.  I'm heading in this afternoon, at which point I will complete Linux testing.
    The only other problem is that vehicles are not working yet.  I'm not sure yet how I will proceed with this.
    Updating C++ Projects
    The following changes are needed to update your C++ projects:
    Visual Studio
    Add these include header search directories:
    $(LeadwerksHeaderPath)\Libraries\NewtonDynamics\packages\thirdParty\timeTracker Add these input libraries:
    newton_d.lib;dContainers_d.lib;dCustomJoints_d.lib; (debug) newton.lib;dContainers.lib;dCustomJoints.lib; (release) Code::Blocks
    Add these include header search directories:
    $(LeadwerksPath)/Include/Libraries/NewtonDynamics/packages/thirdParty/timeTracker You also need the dev files for libcurl:
    sudo apt-get install libcurl4-openssl-dev This is pretty much the finished 4.4, so please test it and post any bug reports you have.  Thank you.
  11. Josh
    Leadwerks Game Engine 4.4 features an upgrade to the latest version of Newton Dynamics, along with a bunch of new features to enhance physics.
    Kinematic Controller
    The new kinematic controller is a joint that lets you specify a position, rotation (Euler or quaternion), or a 4x4 matrix to orient the body to.  You can set the maximum linear and angular force the joint may use to orient the entity.  This allows you to create a kinematic controller that only affects position, only affects rotation, or one that controls both at once.  In the video below I am using a kinematic controller to create a simple IK system with two hinge joints.  The end effector is controlled by the mouse position, while the base entity stays in place, since it has zero (infinite) mass:
    The kinematic controller provides much more stable collisions than the Entity PhysicsSetPosition() and PhysicsSetRotation() commands, and should be used in place of these.  In fact, these commands will be removed from the documentation and should not be used anymore, although they will be left in the engine to ensure your code continues to work.  The FPS player script will be updated to use a kinematic control for objects you are holding, which will eliminate the energetic collisions the script currently produces if you pick up a crate and push it into the wall.
    The new joint commands are as follows:
    static Joint* Kinematic(Entity* entity, const Vec3& position); virtual void SetTargetMatrix(const Mat4& mat); virtual void SetTargetPosition(const float x, const float y, const float z, const float blend = 0.5); virtual void SetTargetPosition(const Vec3& pos, const float blend = 0.5); virtual void SetTargetRotation(const float pitch, const float yaw, const float roll, const float blend = 0.5); virtual void SetTargetRotation(const Vec3& rotation, const float blend = 0.5); virtual void SetTargetRotation(const Quat& rotation, const float blend = 0.5); For improved constistency in the API, the joint SetAngle function will be renamed SetTargetAngle, but a copy of the old command will remain in the engine:
    virtual void SetTargetAngle(const float angle); Joint Friction
    Hinge joints can now accept a friction value to make them more resistant to swinging around.  I used this in the example below to make the joints less "loose", while a kinematic controller positions the green box:
    New Vehicle Model
    Newton 3.14 features a new vehicle model with a realistic simulation of a slip differential.  Power is adjusted to each wheel according to the resistance on each tire.

    Watch closely as the wheels below act just like a real car does when its tires slip:
    The realistic vehicle models gives vehicles a much more visceral and fun feeling.  The new vehicle also uses actual bodies for the tires, instead of convex raycasts, so the sudden bouncing the old vehicles could exhibit if the chassis didn't encompass the tires is eliminated.
    Springs
    Slider and hinge joints now have optional spring behavior you can enable with one command.  Use this to make our own custom suspension system, or anything else you need.
    void SetSpring(const float spring) These changes will be available next week on the beta branch on Steam.
  12. Josh
    A new easy-to-use networking system is coming soon to Leadwerks Game Engine.  Built on the Enet library, Leadwerks networking provides a fast and easy way to quickly set up multiplayer games.  Each computer in the game is either a server or a client.  The server hosts the game and clients can join and leave the game at will.  On the other hand, when the server leaves the game, the game is over!

    Creating a Client
    You can soon create a client with one command in Leadwerks:
    client = Client:Create() To connect to a server, you need to know the IP address of that computer:
    client:Connect("63.451.789.3") To get information from the other computer, we simply update the client and retrieve a message:
    local message = client:Update() if message.id == Message.Connected then print("Connected to server") elseif message.id == Message.Disconnected then print("Disconnected from server") elseif message.id == Message.Chat then print("New chat message: "..message.stream:ReadString()); end You can even send messages, consisting of a simple message ID, a string, or a stream.
    client:Send(Message.Chat,"Hello, how are you today?") There are two optional flags you can use to control the way your messages are sent.  If you specify Message.Ordered, your packets will arrive in the order they were sent (they won't necessarily otherwise).  You can use this for updating the position of an object, so that the most recent information is always used.  The Message.Reliable flag should be used for important messages that you don't want to miss.  UDP packets are not guaranteed to ever arrive at their destination, but messages sent with this flag are.  Just don't use it for everything, since it is slower!
    When we're ready to leave the game, we can do that just as easily:
    client:Disconnect() A dedicated server does not have anyone playing the game.  The whole computer is used only for processing physics and sending and receiving information.  You can create a dedicated server, but it's better to let your players host their own games.  That way there's always a game to join, and you don't have to buy an extra computer and keep it running all the time.
    Creating a Server
    Your game should be able to run both as a client and as a server, so any player can host or join a game.  Creating the game server is just as easy.
    local server = Server:Create(port) Once the server is created, you can look up your IP address and ask a friend to join your game.  They would then type the IP address into their game and join.
    The server can send and receive messages, too.  Because the server can be connected to multiple clients, it must specify which client to send the message to.  Fortunately, the Message structure contains the Peer we received a message from.  A peer just means "someone else's computer".  If your computer is the client, the server you connect to is a peer.  If your computer is the server, all the other clients are peers:
    local message = client:Update() if message.id == Message.Connected then player2 = message.peer end You can use the peer object to send a message back to that computer:
    server:Send(peer, Message.Chat, "I am doing just great! Thanks for asking.") If you want to boot a player out of your game, that's easy too:
    server:Disconnect(peer) The broadcast command can be used to send the same message out to all clients:
    server:Broadcast(Message.Chat, "I hope you are all having a great time in my cool chat program!") Public Games
    You can make your game public, allowing anyone else in the world who has the game to play with you.  You specify a name for your game, a description of your server, and call this command to send a message to the Leadwerks server:
    server:Publish("SuperChat","My SuperChat Server of Fun") All client machines, anywhere in the world, can retrieve a list of public games and choose one to join:
    for n=0,client:CountServers("SuperChat")-1 do local remotegame = client:GetServer(n) print(remotegame.address) print(remotegame.description) end This is a lot easier than trying to type in one person's IP address.  For added control, you can even host a games database on your own server, and redirect your game to get information from there.
  13. Josh
    After a lot of research and development, Leadwerks GUI is almost ready to release.  The goal with this system was to create an in-game GUI that was customizable, extendable, and could also serve as a windowed GUI for application development in the future.
    Widgets
    The GUI system is based on the Widget class.  Once a GUI is created on a rendering context you can add widgets to it.  Each widget is a rectangular container with a padding area.  The widgets can be arranged in a hierarchy and their bounds will clip the contents of their child widgets, both for rendering and mouse interaction.
    The GUI is automatically rendered onto the screen inside the call to Context:Sync(),
    Widget Scripts
    Each widget has a Lua script to control rendering and behavior, similar to the way Lua scripts work in our entity system.  The script assigned to a widget controls what type of widget it is, how it looks, and how it interacts with mouse and keyboard input.  A set of widget scripts are provided to create a variety of controls including buttons, checkboxes, text entry boxes, list boxes, text display areas, choice boxes, sliders, and more.
    You can create your own widget scripts to add new types of controls, like for an RPG interface or something else.  The script below shows how the tabber widget is implemented.
    --Styles if Style==nil then Style={} end if Style.Panel==nil then Style.Panel={} end Style.Panel.Border=1 Style.Panel.Group=2 --Initial values Script.indent=1 Script.tabsize = iVec2(72,28) Script.textindent=6 Script.tabradius=5 function Script:Start() self.widget:SetPadding(self.indent,self.indent,self.tabsize.y+self.indent,self.indent) end function Script:MouseLeave() if self.hovereditem~=nil then self.hovereditem = nil local scale = self.widget:GetGUI():GetScale() local pos = self.widget:GetPosition(true) local sz = self.widget:GetSize(true) self.widget:GetGUI():Redraw(pos.x,pos.y,sz.width,self.tabsize.y*scale+1) --self.widget:Redraw() end end function Script:Draw(x,y,width,height) local gui = self.widget:GetGUI() local pos = self.widget:GetPosition(true) local sz = self.widget:GetSize(true) local scale = self.widget:GetGUI():GetScale() local n local sel = self.widget:GetSelectedItem() --Draw border gui:SetColor(0) gui:DrawRect(pos.x,pos.y+self.tabsize.y*scale,sz.width,sz.height-self.tabsize.y*scale,1) --Draw unselected tabs for n=0,self.widget:CountItems()-1 do if n~=sel then self:DrawTab(n) end end --Draw selected tab if sel>-1 then self:DrawTab(sel) end ---Panel background gui:SetColor(0.25) gui:DrawRect(pos.x+1,pos.y+self.tabsize.y*scale+1,sz.width-2,sz.height-self.tabsize.y*scale-2) end function Script:DrawTab(n) local gui = self.widget:GetGUI() local pos = self.widget:GetPosition(true) local sz = self.widget:GetSize(true) local scale = self.widget:GetGUI():GetScale() local s = self.widget:GetItemText(n) local textoffset=2*scale if self.widget:GetSelectedItem()==n then textoffset=0 end local leftpadding=0 local rightpadding=0 if self.widget:GetSelectedItem()==n then gui:SetColor(0.25) if n>0 then leftpadding = scale*1 end rightpadding = scale*1 else gui:SetColor(0.2) end gui:DrawRect(-leftpadding+pos.x+n*(self.tabsize.x)*scale,textoffset+pos.y,rightpadding+leftpadding+self.tabsize.x*scale+1,self.tabsize.y*scale+self.tabradius*scale+1,0,self.tabradius*scale) gui:SetColor(0) gui:DrawRect(-leftpadding+pos.x+n*(self.tabsize.x)*scale,textoffset+pos.y,rightpadding+leftpadding+self.tabsize.x*scale+1,self.tabsize.y*scale+self.tabradius*scale+1,1,self.tabradius*scale) if self.widget:GetSelectedItem()~=n then gui:SetColor(0) gui:DrawLine(pos.x+n*self.tabsize.x*scale,pos.y+self.tabsize.y*scale,pos.x+n*self.tabsize.x*scale+self.tabsize.x*scale,pos.y+self.tabsize.y*scale) end if self.hovereditem==n and self.widget:GetSelectedItem()~=n then gui:SetColor(1) else gui:SetColor(0.7) end gui:DrawText(s,pos.x+(n*self.tabsize.x+self.textindent)*scale,textoffset+pos.y+self.textindent*scale,(self.tabsize.x-self.textindent*2)*scale-2,(self.tabsize.y-self.textindent*2)*scale-1,Text.VCenter+Text.Center) end function Script:MouseDown(button,x,y) if button==Mouse.Left then if self.hovereditem~=self.widget:GetSelectedItem() and self.hovereditem~=nil then self.widget.selection=self.hovereditem local scale = self.widget:GetGUI():GetScale() local pos = self.widget:GetPosition(true) local sz = self.widget:GetSize(true) self.widget:GetGUI():Redraw(pos.x,pos.y,sz.width,self.tabsize.y*scale+1) EventQueue:Emit(Event.WidgetAction,self.widget,self.hovereditem) end elseif button==Mouse.Right then if self.hovereditem~=self.widget:GetSelectedItem() and self.hovereditem~=nil then EventQueue:Emit(Event.WidgetMenu,self.widget,self.hovereditem,x,y) end end end function Script:KeyDown(keycode) if keycode==Key.Right or keycode==Key.Down then local item = self.widget:GetSelectedItem() + 1 if item<self.widget:CountItems() then self.widget.selection=item local scale = self.widget:GetGUI():GetScale() local pos = self.widget:GetPosition(true) local sz = self.widget:GetSize(true) self.widget:GetGUI():Redraw(pos.x,pos.y,sz.width,self.tabsize.y*scale+1) EventQueue:Emit(Event.WidgetAction,self.widget,item) end elseif keycode==Key.Left or keycode==Key.Up then local item = self.widget:GetSelectedItem() - 1 if item>-1 and self.widget:CountItems()>0 then self.widget.selection=item local scale = self.widget:GetGUI():GetScale() local pos = self.widget:GetPosition(true) local sz = self.widget:GetSize(true) self.widget:GetGUI():Redraw(pos.x,pos.y,sz.width,self.tabsize.y*scale+1) EventQueue:Emit(Event.WidgetAction,self.widget,item) end elseif keycode==Key.Tab then local item = self.widget:GetSelectedItem() + 1 if item>self.widget:CountItems()-1 then item=0 end if self.widget:CountItems()>1 then self.widget.selection=item local scale = self.widget:GetGUI():GetScale() local pos = self.widget:GetPosition(true) local sz = self.widget:GetSize(true) self.widget:GetGUI():Redraw(pos.x,pos.y,sz.width,self.tabsize.y*scale+1) EventQueue:Emit(Event.WidgetAction,self.widget,item) end end end function Script:MouseMove(x,y) local prevhovereditem = self.hovereditem self.hovereditem = nil local scale = self.widget:GetGUI():GetScale() local sz = self.widget:GetSize(true) if x>=0 and y>=0 and x<sz.width and y<self.tabsize.y*scale then local item = math.floor(x / (self.tabsize.x*scale)) if item>=0 and item<self.widget:CountItems() then self.hovereditem=item end end if self.hovereditem==self.widget:GetSelectedItem() and prevhovereditem==nil then return end if self.hovereditem==nil and prevhovereditem==self.widget:GetSelectedItem() then return end if prevhovereditem~=self.hovereditem then local pos = self.widget:GetPosition(true) local sz = self.widget:GetSize(true) self.widget:GetGUI():Redraw(pos.x,pos.y,sz.width,self.tabsize.y*scale+1) end end Widget Rendering
    Widgets are buffered and rendered with an advanced system that draws only the portions of the screen that need to be updated.  The GUI is rendered into a texture, and then the composite image is drawn onscreen.  This means you can have very complex interfaces rendering in real-time game menus with virtually no performance cost.
    By default, no images are used to render the UI so you don't have to include any extra files in your project.
    Widget Items
    Each widget stores a list of items you can add, remove, and edit.  These are useful for list boxes, choice boxes, and other custom widgets.
    GUI Events
    Leadwerks 4.4 introduces a new concept into your code, the event queue.  This stores a list of events that have occurred.  When you retrieve an event it is removed from the stack:
    while EventQueue:Peek() do local event = EventQueue:Wait() if event.source == widget then print("OK!") end end Resolution Independence
    Leadwerks GUI is designed to operate at any resolution.  Creation and positioning of widgets uses a coordinate system based on a 1080p monitor, but the GUI can use a global scale to make the interface scale up or down to accommodate any DPI including 4K and 8K monitors.  The image below is rendering the interface at 200% scaling on a 4K monitor.

    A default script will be included that you can include from Main.lua to build up a menu system for starting and quitting games, and handling common graphical features and other settings.

    Leadwerks GUI will be released in Leadwerks Game Engine 4.4.
  14. 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.
  15. Josh
    The forum software has been updated to a major new version.  This completes my effort to give the entire website responsive design, and ensures we continue to receive security updates.  The responsive design part is really for SEO, but it is kind of cool to be able to browse the entire site on your phone without zooming in.
    Documentation has been switched over to the new system here, which is independent from the forum software:
    https://www.leadwerks.com/learn
    The entire site is now using SSL on every page, again for SEO purposes.
    There are a few bits that need improvement, but overall it's a solid upgrade and I think you will find the new site to be very helpful.  I've added a new Q & A forum where you can get technical assistance and rate the answers.  Code formatting has been improved a lot, and the notifications system does a really good job of catching replies to your posts.  (If you're receiving a lot of emails you can disable this in your account settings.)
    Finally, you can now set a cover image to show at the top of your profile, which is pretty cool.
  16. Josh
    I've taken your suggestions and incorporated the fixes into the new documentation system here:
    https://www.leadwerks.com/learn
     
    All classes and commands are alphabetized, with classes listed first.
     
    All pages in the API Reference should be working now.
     
    If a page does not appear in the old docs, or if a command does not have an example, it will not appear in the new docs, as I am not changing the content right now.
     
    Please let me know if the table of contents of pages have any errors.
  17. Josh
    I'm asking the community to look over the new documentation system and find any mistakes before I sign off on the project. The tutorials are not completed. The API Reference should be completed, although the main API Reference page is not showing anything right now.
    https://www.leadwerks.com/learn
     
    Here are my notes to the developer who is working on the docs:
     
    Please review the material and make sure everything meets your satisfaction, and leave your comment below if needed. I figure many sets of eyes will be more efficient than just me looking through it. This is your documentation, and I want to make sure it's the best for you. Thanks for your help!
  18. Josh
    The table of contents and index can now be used to navigate to a page:
    https://www.leadwerks.com/learn
     
    Search is also working.
     
    Almost all of the commands are filled in, with examples for Lua and C++, updated to use the standard main() entry point, or in the case of Lua, just a main script with no function.
     
    Function arguments and syntax will also be displayed in C++ or Lua data types. So if you have the docs set to Lua, you will just see "number" instead of "const float blah blah blah..."
     
    I am going to review the material and then I will ask the community to go through it before I give my freelancer the final OK.
     

  19. Josh
    The new docs system is mostly working now:
    https://www.leadwerks.com/learn?page=API-Reference_Object_Entity_SetPosition
     
    Features:
    Treeview list of all tutorials and API classes and functions.
    Alphabetical index (generated automatically from the table of contents).
    Search (with autogenerated search index).
    Switch back and forth between languages, with cookies to remember your preference.
    Entire examples are automatically selected when you click on them.

    Todo:
    Table of contents doesn't yet navigate.
    I would like to pretty-up the URLs using .htaccess rewrite rules.

     
    Documentation is being loaded from XML files. I find this easier than a database, which is quite opaque to me. You can access some of the XML files being used here. MartyJ even made a cool template for the data on his site here.
     
    At this point I think I can outsource the actual content creation to Upwork. This will involve copying and formatting our existing documentation into XML files, as well as updating all the examples to work by just using a main() function instead of the old App() class that we don't really need. I will be posting the job on that site soon.
  20. Josh
    I've been getting good results storing documentation data in XML files. This allows me to make changes to the way documenation is displayed, without updating hundreds or thousands of pages. I can also write documentation without as much HTML markup. For example, bullets can automatically be inserted in line breaks for the syntax info. Javascript-based search engines usually require all page contents to be store in an array, so this gives us an easy way to collect that data.
     
    Since XML is a known standard, this makes it a bit easier to outsource the documentation content.
     
    Here is a sample file I am working with.

    <?xml version="1.0"?>
    <page>
    <title>SetPosition</title>
    <description>Sets the position of an entity in 3-dimensional space, using local or global coordinates.</description>
    <cppsyntax>
    void Entity::SetPosition(const float x, const float y, const float z, const bool global = false)
    void Entity::SetPosition(const Vec3& position, const bool global = false)
    </cppsyntax>
    <luasyntax>
    nil Entity:SetPosition(number x number y, number z, bool global = false)
    nil Entity:SetPosition(Vec3 position, bool global = false)
    </luasyntax>
    <parameters>
    x: X component of the specified position.
    y: Y component of the specified position.
    z: Z component of the specified position.
    position: the position to set.
    global: indicates whether the position should be set in global or local space.
    </parameters>
    <remarks>
    An entity can be positioned in local or global coordinates. Local coordinates are relative to the entity parent's space.
     
    If the entity does not have a parent, local and global coordinates are the same.
     
    <img src='img/552px-Space.png' />
     
    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.
    <img src='img/image2.jpg' />
    </remarks>
    <luaexample>
    --Create a window
    window = Window:Create()
    context = Context:Create(window)
    world = World:Create()
    local camera = Camera:Create()
    camera:SetRotation(35,0,0)
    camera:Move(0,0,-6)
    local light = DirectionalLight:Create()
    light:SetRotation(35,35,0)
    --Create a model
    model = Model:Box()
    while true do
    if window:Closed() or window:KeyHit(Key.Escape) then return false end
    model:SetPosition(Math:Sin(Time:GetCurrent()/10.0),0,0)
    Time:Update()
    world:Update()
    world:Render()
    context:Sync()
    end
    </luaexample>
    <cppexample>
    #include "Leadwerks.h"
    using namespace Leadwerks;
    int main(int argc, const char *argv[])
    {
    Leadwerks::Window* window = Window::Create();
    Context* context = Context::Create(window);
    World* world = World::Create();
    Camera* camera = Camera::Create();
    camera->SetRotation(35, 0, 0);
    camera->Move(0, 0, -4);
    Light* light = DirectionalLight::Create();
    light->SetRotation(35, 35, 0);
    //Create a model
    Model* model = Model::Box();
    while (true)
    {
    if (window->Closed() || window->KeyDown(Key::Escape)) return false;
    model->SetPosition(Math::Sin(Time::GetCurrent() / 10.0), 0, 0);
    Leadwerks::Time::Update();
    world->Update();
    world->Render();
    context->SetBlendMode(Blend::Alpha);
    context->DrawText(model->GetPosition().ToString(), 2, 2);
    context->Sync();
    }
    return 0;
    }
    </cppexample>
    </page>
     
    Even the table of contents can be store in XML, which provides a listing of all pages and a way to automatically create the search index and a list of all topics:

    <?xml version="1.0"?> <contents>
    <topics>
    <topic>
    <title>Tutorials</title>
    <openstate>true</openstate>
    <topics>
    <topic>
    <title>1. Editor</title>
    <openstate>true</openstate>
    <topics>
    <topic><title>1.1 Editor Interface</title><openstate>true</openstate></topic>
    <topic><title>1.2 Scene Panel</title></topic>
    <topic><title>1.3 Textures</title></topic>
    <topic><title>1.4 Materials</title></topic>
    <topic><title>1.5 Models</title></topic>
    <topic><title>1.6 Terrain</title></topic>
    </topics>
    </topic>
    <topic>
    <title>2. Games</title>
    <topics>
    <topic><title>2.1 Marble Platformer</title></topic>
    </topics>
    </topic>
    <topic>
    <title>3. Lua Programming</title>
    <topics>
    <topic><title>3.1 Introduction to Lua</title></topic>
    </topics>
    </topic>
    <topic>
    <title>4. C++ Programming</title>
    <topics>
    <topic><title>4.1 Introduction to C++</title></topic>
    </topics>
    </topic>
    </topics>
    </topic>
    <topic>
    <title>Script Reference</title>
    <topics>
    <topic><title>AI</title></topic>
    <topic><title>Analytics</title></topic>
    </topics>
    </topic>
    <topic>
    <title>API Reference</title>
    <topics>
    <topic>
    <title>Object</title>
    <openstate>true</openstate>
    <topics>
    <topic><title>Asset</title></topic>
    <topic><title>Analytics</title></topic>
    </topics>
    </topic>
    </topics>
    </topic>
    </topics>
    </contents>
  21. Josh
    I'm not entirely happy with the output of HelpNDoc for a few reasons. It outputs a great variety of formats, but doesn't do a spectacular job of any. I hoped to use CHM for local help but these do not scale well on high-resolution displays, and the technology will not be updated by Microsoft. HelpNDoc's HTML output is okay but not great, so I started experimenting with my own HTML to get exactly the look and functionality I want.
     
    RIght now I have a javascript-based treeview for navigation, breadcrumb links, syntax highlighting for Lua and C++, responsive design, the ability to switch between C++ and Lua documentation on the same page, and the code examples select the entire block when you click on them. There's still more work to do, but so far this is the most promising method that is giving the best results. I am not sure yet if this will be a lot of PHP files or a utility program that spits out a bunch of HTML files.
     

     

     
    I'm going to focus on the format of the data and then I'll post a job on Upwork and find someone else to do the grunt work of filling in all the content.
  22. Josh
    Our website has been updated with a new look and responsive design. Here are a few highlights.
     
    Landing page:

     
    Product pages:

     
    Screenshots used in the site from games will display the title and author when you hover the mouse over them.
     
    Responsive layout scaled for phones:

     
    Clearer writing that says exactly what Leadwerks does and who it is for:

     
    Dark gallery and video pages:

     
    Sleek screenshot pages:

     
    I left the Workshop pages as-is for now. The forum software is going to be updated and a new skin will be designed for the new version of the community software.
  23. Josh
    I have a few preview images to show you my progress with the implementation of my design for the website refresh. Enjoy.
     

     

     

     
    Here's a new version of the view image page. It's pretty bold. The image stretches to its max dimensions, and a dark background is shown beyond that.
     

     
    Working with a dark background for the video and gallery pages, and it feels pretty natural. Information pages with lots of text use the light theme, while image-heavy artsy sections use a dark theme so you can focus on the content.
     

     
    This is close to finished:
     

  24. Josh
    Here are some concepts I came up with for the site redesign.
     

     

     

     
    The bold no-bull**** interface of itch.io inspired this design:
     

     
    I think what will work best is if the designer takes my rough sketches, turns it into a clean design, and implements it with clean code.
     
    I don't think we can change the whole site over at once without me losing control of the creative process and having runaway costs. I want to focus on the pages I have shown here and establish a foundation I can experiment with and iterate on.
  25. Josh
    With the upcoming uncertainty that Steam Direct introduces to Steam sellers, I am putting more effort into our website, because it may be the primary channel through which new users discover Leadwerks in the future. Although the functionality of our site is awesome, the visual design is rather outdated and it's time for an actual designer to take over. I've got a rough idea of what I want the site to look like, but beyond that I am happy to let someone else figure it out.
     



     
    I plan on the design using lots of big images, and we have a large collection of high-res renders made with Leadwerks that will look great.
×
×
  • Create New...