Jump to content

Josh

Staff
  • Posts

    23,224
  • Joined

  • Last visited

Blog Entries posted by Josh

  1. Josh
    Now that we have our voxel light data in a 3D texture we can generate mipmaps and perform cone step tracing. The basic idea is to cast a ray out from each side of each voxel and use a lower-resolution mipmap for each ray step. We start with mipmap 1 at a distance that is 1.5 texels away from the position we are testing, and then double the distance with each steo of the ray. Because we are using linear filtering we don't have to make the sample coordinates line up exactly to a texel center, and it will work fine:

    Here are the first results when cone step tracing is applied. You can see light bouncing off the floor and reflecting on the ceiling:

    This dark hallway is lit with indirect lighting:

    There's lots of work left to do, but we can see here the basic idea works.
  2. Josh
    As described earlier, Oculus Rift support is now enabled on the beta branch on Steam!
     


    Enabling VR
    To enable VR in any game, just add the Window::VRDisplay flag in the creation flags in the Window::Create() function:
    Window* window = Window::Create("My VR Game",0,0,1024,768,Window::Titlebar|Window::VRDisplay);
     
    Or in Lua:

    local window = Window:Create("My VR Game",0,0,1024,768,Window.Titlebar + Window.VRDisplay)  
    (If you are using a Lua project, you must update the project to get the new executables.)
     
    It was very easy to add VR to my asteroids game. You can play it here:
    http://www.leadwerks.com/werkspace/topic/11112-asteroidsvr/#entry80913

    New Commands
    There are only a few new commands to learn:
    Vec3 VR::GetHeadRotation()
    Use this to modify the way your controls work. For example, use the player's head rotation to change the direction they move in when a key is pressed. You do not need to rotate the camera yourself, as this is done automatically.
     

    Vec3 VR::GetHeadPosition()
    I can't really think of any good use for this command, since it is all automatic. If you do any line of sight tests I suppose it would be useful.
     

    void VR::Recenter()
    Recenters the headset orientation according to the current position and rotation.
     

    void VR::HideWarning()
    Disable the Oculus warning popup, typically by pressing space or another key.

    Updating Existing C++ Projects
    Because this involves an additional C++ library, you must modify any existing C++ projects to compile with the beta build. To do this, right-click on the project name in the solution explorer and select Properties to open the properties window. Go to the linker settings and edit the Additional Dependencies value. Add the following line to the release build settings:  
    Apply the changes and then add the following line to the debug build settings, in the same place:
     
    You must also modify the file main.cpp. Open this file and find the line that looks like this:

    System::SaveSettings(settingsfile);
     
    Add this line right after it:

    System::Shutdown();
     
    Your entire main,cpp will look like this:

    #ifndef OS_IOS #ifndef _DLL #ifndef BUILD_STATICLIB #include "App.h" #endif #endif using namespace Leadwerks; void DebugErrorHook(char* c) { Leadwerks::System::Print(c); //========================================================================================= //========================================================================================= exit(1);//<--------------------------- Add a breakpoint here to catch errors //========================================================================================= //========================================================================================= } #ifdef __APPLE__ int main_(int argc,const char *argv[]) { #else int main(int argc,const char *argv[]) { #endif //Load saved settings std::string settingsfile = std::string(argv[0]); settingsfile = FileSystem::StripAll(settingsfile); if (String::Right(settingsfile, 6) == ".debug") settingsfile = String::Left(settingsfile, settingsfile.length() - 6); std::string settingsdir = FileSystem::GetAppDataPath(); #ifdef __linux__ #ifndef __ANDROID__ settingsdir = settingsdir + "/." + String::Lower(settingsfile); #else settingsdir = settingsdir + "/" + settingsfile; #endif #else settingsdir = settingsdir + "/" + settingsfile; #endif if (FileSystem::GetFileType(settingsdir) == 0) FileSystem::CreateDir(settingsdir); settingsfile = settingsdir + "/" + settingsfile + ".cfg"; System::LoadSettings(settingsfile); //Load command-line parameters System::ParseCommandLine(argc, argv); //Add debug hook for catching errors Leadwerks::System::AddHook(System::DebugErrorHook,(void*)DebugErrorHook); //Load any zip files in main directory Leadwerks::Directory* dir = Leadwerks::FileSystem::LoadDir("."); if (dir) { for (int i=0; i<dir->files.size(); i++) { std::string file = dir->files[i]; if (Leadwerks::String::Lower(Leadwerks::FileSystem::ExtractExt(file))=="zip") { Leadwerks::Package::Load(file); } } delete dir; } #ifdef DEBUG std::string debuggerhostname = System::GetProperty("debuggerhostname"); if (debuggerhostname!="") { //Connect to the debugger int debuggerport = String::Int(System::GetProperty("debuggerport")); if (!Interpreter::Connect(debuggerhostname,debuggerport)) { Print("Error: Failed to connect to debugger with hostname \""+debuggerhostname+"\" and port "+String(debuggerport)+"."); return false; } Print("Successfully connected to debugger."); std::string breakpointsfile = System::GetProperty("breakpointsfile"); if (breakpointsfile!="") { if (!Interpreter::LoadBreakpoints(breakpointsfile)) { Print("Error: Failed to load breakpoints file \""+breakpointsfile+"\"."); } } } else { // Print("No debugger hostname supplied in command line."); } #endif App* app = new App; if (app->Start()) { while (app->Loop()) {} #ifdef DEBUG Interpreter::Disconnect(); #endif //Save settings delete app; System::SaveSettings(settingsfile); System::Shutdown(); return 0; } else { #ifdef DEBUG Interpreter::Disconnect(); #endif return 1; } } #endif
     
    That's it! Have fun and let me know what you come up with!
  3. Josh
    The clustered forward renderer in Leadwerks 5 / Turbo Game Engine required me to implement a texture array to store all shadow maps in. Since all shadow maps are packed into a single 3D texture, the shader can access all required textures outside of the number of available texture units, which only gives 16 guaranteed slots.
    I realized I could use this same technique to pack all scene textures into a few arrays and completely eliminate the overhead of binding different textures. In order to do this I had to introduce some restrictions. The max texture size, by default, is 4096x4096. Only two texture formats, RGBA and DXT5, are supported. Other texture formats will be converted to RGBA during loading. If a texture is smaller than 1024x1024, it will still take up a layer in the 1024x1024 texture array.
    This also makes texture lookups in shaders quite a bit more complicated.
    Before:
    fragColor *= texture(texture0,texcoords0); After:
    fragColor *= texture(textureatlases[materialdefs[materialid].textureslot[0]],vec3(texcoords0,materialdefs[materialid].texturelayer[0])); However, making shaders easy to read is not a priority in my design. Performance is. When you have one overarching design goal these decisions are easy to make.
    Materials can now access up to 256 textures. Or however many I decide to allow.
    The real reason for this is it will help support my goal to render the entire scene with all objects in just one or a few passes, thereby completely eliminating all the overhead of the CPU/GPU interaction to reach 100% GPU utilization, for ultra maximum performance, to eliminate VR nausea once and for all.
    Also, I went out walking yesterday and randomly found this item in a small shop.

    There's something bigger at work here:
    Most of my money comes through Steam. Valve invented the technology the HTC Vive is based on. Gabe Newell gave me the Gigabyte Brix mini PC that the new engine architecture was invented on. The new engine makes VR performance 10x faster. If this isn't a clear sign that divine providence is on our side, I don't know what is.
  4. Josh
    I'm wrapping up the new multiplayer capabilities for Leadwerks 4.6, and I am very excited about what this offers developers.
    We saw previously how the lobby system is used to create or join games, and how to retrieve Steam IDs for messaging. Once we have joined a game we can start sending messages with the P2P::Send command, which includes a few overloads:
    static bool P2P::Send(uint64 steamid, const int messageid, const int channel = 0, const int flags = 0); static bool P2P::Send(uint64 steamid, const int messageid, std::string& data, const int channel = 0, const int flags = 0); static bool P2P::Send(uint64 steamid, const int messageid, Bank* data, const int channel = 0, const int flags = 0); static bool P2P::Send(uint64 steamid, const int messageid, Stream* data, const int channel = 0, const int flags = 0); static bool P2P::Send(uint64 steamid, const int messageid, const void* data, const int size, const int channel = 0, const int flags = 0); Each message has an ID and can be followed by additional data in the form of a string, bank, or stream. Voice chat is handled automatically, but the rest is up to you to decide what data the messages should contain. I provide examples for text chat, joining and leaving a game, and movement.
    Receiving messages from other players is extremely simple:
    static Message* P2P::Receive(const int channel = 0); The message object has information contained within it:
    class Message { public: int id; Stream* stream; uint64 userid }; We can evaluate messages based on their ID. For example, here is a text chat message being received:
    auto message = P2P::Receive() if (message) { if (message->id == MESSAGE_CHAT && message->stream != nullptr) { Print(message->stream->ReadString()); } } A new multiplayer game template will be included, with out-of-the-box support for text and voice chat, public servers, and player movement.

    You can download the test app and try it out here:
    Thanks to everyone who has helped me test this!
  5. Josh
    After observing the behavior of the previous test, I rearranged the threading architecture for even more massive performance gains. This build runs at speeds in excess of 400 FPS with 100,000 entities....on Intel integrated graphics!

    I've had more luck with concurrency in design than parallelism. (Images below are taken from here.)
    Splitting the octree recursion up into separate threads produced only modest gains. It's difficult to optimize because the sparse octree is unpredictable.

    Splitting different parts of the engine up into multiple threads did result in a massive performance boost.

    The same test in Leadwerks 4 runs at about 9 FPS. making Leadwerks 5 more than 45 times faster under heavy loads like this.
    Alpha subscribers can try the test out here.
  6. Josh
    I'm happy to say I nailed the design of the Leadwerks 3 workflow overall, but there are a couple of places where there's still some friction. Because the community is going to soon be moving a lot of content into the Steam Workshop, now is a good time to take a look at those issues. It's also smart to address them before the Blender exporter is written.
     
    A new update has been pushed out to the beta branch on Steam that contains a lot of improvements in this area. However, both the map and model file format version have been incremented to support new features. That means your work saved in the beta build can only be opened by the newer builds, until a full update goes out.
     
    FBX Material Generation
    When FBX files are imported, their material files will now be automatically generated, if they aren't already present. A shader will be chosen based on what textures are present in the file, and it will even detect animated models and assign an animation shader when appropriate. This makes it dead simple to get models into Leadwerks.
     
    Collision Shapes in Models
    This came up because I found that 95% of the models I import are just static props that need a simple compound collision shape. First I tried using a convex decomposition algorithm in HACD, the same library Bullet Physics uses. This is a pretty impressive bit of code that takes a polygonal mesh and turns it into an approximate series of convex pieces:

     
    However, trying to generate low-poly physics geometry from a visual mesh had unpredictable results. Trying to generate a convex decomposition from a concave hand-modeled mesh was even worse; you had the tedium of hand-modeling the physics shape, combined with the unpredictability of an automated algorithm.
     
    So I abandoned that approach and decided to make something as simple as possible, that would handle most of the cases the average user encounters. You can now add solid convex pieces in your models, and as long as the name of the limb is "collision" it will get loaded as part of a default physics shape. The existing system of physics shapes remains unchanged, and all your existing objects will still work just as before.

     
    Multi-Animation FBX Files
    Leadwerks will now load multiple animations from FBX files, and will use the name of the animation sequence found in the files. Oh, by the way we now support...
     
    Named Animations
    Animations can now have a name assigned to them, and the animation commands now include an overload to specify a sequence name like "Run", "Walk", "Idle", etc. This makes it easy to mix and match character models because you don't have to worry about which animation index you want to use.
     
    This video shows how these great new features work:


     
    But wait, there's more!
     
    Script-based Post Effects
    We've also got script-based post effects in the editor! This allows a wider range of effects than pure shader effects did. You can modify the script in real-time and see the results immediately, although it is possible to crash the editor if you aren't careful:
     
    A samples bloom script effect is included that is a combination of mine and Shadmar's code. It is interesting that the real-time shader visualization of Leadwerks 3 has allowed us to create a much higher-quality bloom effect than what Leadwerks 2 could do. This is because I could quickly modify code, press F5, and see the results right away instead of having to restart the editor just to change a shader.
     

     
    Workshop files can now be selected in the file open dialog, so I expect to see lots of great new effects come from the community soon, especially from Shadmar and Klepto2.
     
    Batch Material Generation
    You just imported a folder full of textures and you don't want to create materials one-by-one. What do you do? The new Tools > Batch Generate Materials menu is the right solution for you! This feature will auto-generate materials for the entire current directory shown in the asset browser. And if you want some quick normal maps, just select the Tools > Batch Generate Normal Maps menu item. This will allow you to quickly import tons of textures and publish them to the Leadwerks Workshop in no time at all.
  7. Josh
    A new update is available for Leadwerks Game Engine 4.6 beta. This fixes many bugs.
    Slow kinematic joint rotation Heightmap import flipped Map switching crashes in VR Project Manager cancel button bug The Zone DLC map failing to load Ball joints not working Take a look at the bug reports forum to see all the recent fixes in the engine and documentation.
    This is a full update, with new builds for Windows and Linux, for C++ and Lua games. You can opt into the beta branch on Steam to get the update.
  8. Josh
    The lean startup methodology posits that life is to short to build a product no one wants. The old way of doing business involved a limited number of companies with very high barriers to entry adopting a "push" strategy. Sears could effectively create demand for a product by choosing what they wanted to feature prominently in their mail-order catalog. Television advertisers could craft a promotional strategy that would reliably result in a certain response. Those days are over, fortunately, but the old way of thinking is still with us.
     
    Today consumers have more choices than ever before. The low cost of internet hosting has led to a long tail of diverse products. In gaming, marketplaces like Steam and mobile app stores have allowed indie game developers to specialize in various niches and deliver value to customers that would otherwise go unserved. This is really awesome, but the old "push" methodology isn't going to work here.
     
    The best approach to succeed in this new multifaceted market is to test markets. This consists of three steps:
    Identify various market segments.
    Show members of each market segment a mockup of a product geared towards them.
    Collect data and decide which segment to pursue.

     
    Leadwerks Software used this method to identify the Linux market segment. I did not know if a Linux product would be successful, so I put up a Kickstarter campaign and received affirmation from the market. Making Leadwerks run on Linux was a lot of work. I didn't want to do all that, just to find out that nobody wanted it. So testing the market gave me enough of a response I was confident to go forward with the project. The idea is that it's easier to get feedback and change direction early than to build a full product based on a lot of wrong assumptions about what the customer wanted. So you actually want to sell a product that doesn't exist yet. If people won't buy it, it saves you the trouble of making the thing and having it fail.
     
    As a PC developer, the best tool you have to test the market is Steam Greenlight. You can post a Greenlight Concept well in advance of actually writing any code and get a feel for how people respond to your ideas. It doesn't cost anything, and will help you gather feedback and build a fan base early on.
     
    ScrotieFlapWack, Digman, and Rick are using this approach to promote their game Phobia on Steam early on. At this point all we've seen are a menu video, some prototype gameplay, and a few early artwork pieces. But what's important is they're establishing a concept...which is exactly what Greenlight Concepts are for.
     
    franck22000 is a bit further ahead in the game, but he's still taking care to establish a fan base before his final release. Dangerous Rays has been released in alpha form and he's getting feedback from his users long before publishing the final release.
     
    Menu screens are a great way to communicate the concept of a game. They usually include music, a logo, and a background scene that communicates the environment of a game. Without knowing any of the details, you can instantly envision a vague idea of what the final game might be like. I picture Phobia as being sort of like the Amnesia series in a modern industrial setting:

    http://www.youtube.com/watch?v=m1q0XJdT22U
     
    I picture Contained as being an action shooter that's a cross between Killing Floor and Doom 3:


     
    The intent of the authors was probably a little different from my interpretation, but that doesn't matter. I saw enough to be inspired. And when people are inspired by your idea, a funny thing happens. Suddenly you will find that they want to join your team and help bring your idea to reality. So a concept can help you rally a team of people to help.
     
    If you have a compelling idea of the game you want to make, it pays to start your marketing early. By posting a Greenlight Concept early on, you will save time, attract talented people who want to help, and establish a user base that loves what you're doing. Life is too short to build a game nobody wants.
  9. Josh
    Leadwerks Game Engine 5 moves Leadwerks forward into the future with massive performance increases and more advanced features, it also makes game development easier than ever before with three great new programming features.
    Shared Pointers
    I could write a whole article about the benefits of shared pointers.  Shared pointers are basically a simple form of garbage collection that relieves you from the need to manually delete objects, but doesn't suffer from the slow speed of full garbage collection.like C# uses.
    In Leadwerks 4 we need to keep track of object reference counts by using the Release command when we are done with them:
    Texture *texture = Texture::Load("Materials/Bricks/brick01.tex"); Material *material = Material::Create() material->SetTexture(texture); Model *model = Model::Box(); box->SetMaterial(material); material->Release(); if (texture) texture->Release(); In Leadwerks 5 we never have to worry about calling Release() or (less commonly used) AddRef() because reference counts of shared pointers are tracked automatically.  The code below would cause a memory leak in Leadwerks 4 but is perfectly fine to use in Leadwerks 5:
    auto material = Material::Create() material->SetTexture(Texture::Load("Materials/Bricks/brick01.tex")); auto model = Model::Box(); box->SetMaterial(material); This even works with entities.  As soon as a variable goes out of scope the object is deleted:
    if (true) { auto model = Model::Box(); }// automatic deletion occurs here
    We can prevent deletion of an object by either keeping the variable in our code, or keeping a container that holds it.  In the case of map loading, the Map:Load() will return a structure that contains all the entity handles so they stay in memory.  Want to delete a camera?  It's easy:
    camera = NULL;// poof! There are still some details to work out but so far this approach is working great.
    Another great benefit of shared pointers is that you can completely eliminate the need for big complicated destructors like this one.  All those objects will be automatically deleted when they go out of scope:
    OpenGLCamera::~OpenGLCamera() { for (int i=0; i<8; ++i) { if (shader_ambient[i]) { shader_ambient[i]->Release(); shader_ambient[i]=NULL; } for (int n = 0; n < 2; ++n) { for (int q = 0; q < 2; ++q) { for (int p = 0; p < 2; ++p) { if (shader_directional[i][n][q][p]) { shader_directional[i][n][q][p]->Release(); shader_directional[i][n][q][p] = NULL; } if (shader_directional_volume[i][n][q][p]) { shader_directional_volume[i][n][q][p]->Release(); shader_directional_volume[i][n][q][p] = NULL; } if (shader_point[i][n][q][p]) { shader_point[i][n][q][p]->Release(); shader_point[i][n][q][p] = NULL; } if (shader_point_volume[i][n][q][p]) { shader_point_volume[i][n][q][p]->Release(); shader_point_volume[i][n][q][p] = NULL; } if (shader_spot_volume[i][n][q][p]) { shader_spot_volume[i][n][q][p]->Release(); shader_spot_volume[i][n][q][p] = NULL; } if (shader_environment[i][n][q][p]) { shader_environment[i][n][q][p]->Release(); shader_environment[i][n][q][p] = NULL; } for (int usedecal = 0; usedecal < 2; usedecal++) { if (shader_spot[i][n][q][usedecal][p]) { shader_spot[i][n][q][usedecal][p]->Release(); shader_spot[i][n][q][usedecal][p] = NULL; } } } } } } } That entire function has been commented out in Leadwerks Game Engine 5.
    Finally, shared pointers eliminate a problem that is the bane of all programmers' existence.  When used correctly, you can you can say goodbye to invalid and uninitialized pointers forever!  Yet shared pointers, unlike garbage collection, are fast to use and don't slow your game down.
    Automatic Typing
    The use of C++11 in Leadwerks Game Engine 5 gives us automatic typing of variables with the auto keyword.  We can simply something like this:
    shared_ptr<Model> model = Model::Box(); To just this:
    auto model = Model::Box(); If you have long complicated type names this makes life a million times easier:
    for (std::list<shared_ptr<Entity> >::iterator it = list.begin(); it!= list.end(); it++) { shared_ptr<Entity> entity = *it; entity->SetPosition(1,2,3); } Here's the same code simplified with auto (and range-based loops thanks to @Roland and @thehankinator.  Look how much more readable it is:
    for (auto entity : list) { entity->SetPosition(1,2,3); } This isn't completely new, but Leadwerks 5 is the first version built exclusively for C++11 features.
    More Explicit API
    Leadwerks 4 uses several bound states to store a current world and context.  These are global variables that are mostly invisible to the user.  This causes problems with multithreaded programming because two threads might try to change or access the bound state at the same time.
    World *world1 = World::Create(); Model *model1 = Model::Box(); World *world2 = World::Create(); Model *model2 = Model::Box(); World::SetCurrent(world1); Model *model3 = Model::Box(); Quick, which world does "model3" belong to?  Leadwerks 5 gets rid of the concept of bound states and requires you to explicitly specify the object you want to use.  Here's how the above code would look in Leadwerks 5:
    auto world1 = World::Create(); auto model1 = Model::Box(world1); auto world2 = World::Create(); auto model2 = Model::Box(world2); auto model3 = Model::Box(world1); Not only is the second example easier to understand, it's also one line shorter.
    In a similar manner, the idea of a "current" context will be eliminated.  When you render a world you will need to explicitly specify which context to render to:
    world->Render(context) These programming features will make it easier than ever to code games with Leadwerks.
  10. Josh
    Fall is in the air.  The leaves are changing colors, people are bundling up, and game developers are itching to try their hand at another community game tournament.  How does it work?  For 30 days, the Leadwerks community builds small playable games.  Some people work alone and some team up with others.  At the end of the month, on Halloween day, we release our projects to the public and play each other's games.  The point is to release something short and sweet with a constrained timeline, which has resulted in many odd and wonderful mini games for the community to play.
    WHEN: The tournament begins Sunday, October 1, and ends Tuesday, October 31st at the stroke of midnight.
    HOW TO PARTICIPATE: Publish your Halloween-or-other-themed game to Steam Workshop or upload it to itch.io before the deadline. You can work as a team or individually. Use blogs to share your work and get feedback as you build your game. If you need models for your game, we've got a Halloween Model Pack for you to use for free from Leadwerks Workshop.
    Games must have a preview image, title, and contain some minimal amount of gameplay (there has to be some way to win the game) to be considered entries. It is expected that most entries will be simple, given the time constraints.
    On November 1st we will post a roundup blog featuring your entries.
  11. Josh
    I'm back home now, relaxing after a long week away. And what a week it was!
    I got my lecture out of the way on Monday. I felt like I was pretty well prepared, but some acoustical problems in the hall really made it very difficult to concentrate, plus I was pretty nervous about talking in front of about 600 people. I did it though, and hope to do another one next year. The recorded lecture should be available in the GDC vault.
    During my talk, I described my experience as a PC developer, moving into mobile, and making a renderer to be cross-platform and scalable. I also showed off a deferred renderer running on the iPad. A couple of interesting things came out of this research. FIrst, I figured out a few details on how to render to a texture. This forms the basis of project shadows and post-effects on mobile. More importantly, I discovered that mobile hardware is better than PC hardware at rendering to textures! This is what the performance of a deferred and forward renderer looks like on the PC:

    A deferred renderer scales better, but takes an initial hit on performance, which is why Leadwerks 2 has such heavy hardware requirements, and the framerate never goes above 300 or so. It turns out, PowerVR graphics hardware for mobile can render to a texture at no cost. We get the scalability of a deferred renderer, without that initial hit on performance:

    I talked to some engineers with Mali, and they told me their graphics chips work the same way. The implications of this are pretty staggering. It means that per unit processing horsepower, mobile graphics hardware is actually more capable than PC graphics hardware, and AAA graphics on mobile are right around the corner.
    We set up our booth on Tuesday before the show. This was my first time going behind the scenes, so it was a totally different view of the conference. Before the expo floor opened, I asked the Oculus Rift guys if I could try their new toy out, and they let me demo it. I was only disappointed that they used a Mech-style game and the cockpit took up most of my peripheral vision, which eliminated any reason to look around and experience the full VR effect.
    I had a lot of meetings with different graphics hardware manufacturers, got some good technical info, and scored some free hardware for development and testing.
    The response we got from the attendees was really good. When we explained what Leadwerks 3 was, everyone got it right away, and were enthusiastic about what they saw. Here's some footage from the show:
    It was a push to get Leadwerks 3 out in time for the GDC, but well worth it. We got the word out about Leadwerks 3, got a lot of positive feedback and a better idea of what our target market needs, and put Leadwerks on the radar of some very big companies in the industry.
    Starting Monday, bug fixes have top priority, and I will start adding some graphical enhancements that will make a big enhancement to the renderer.
  12. Josh
    I've implemented DPI scaling into Leadwerks GUI (beta branch, Lua on Windows only).
     
    To set the GUI scale you just call gui:SetScale(scalefactor). A scale factor greater than one will make it bigger, a scale factor less than one will make it smaller. There is no limit to the range you can set, but negative numbers are probably not good.
     
    Scaling causes the GUI to be drawn at a different size, but widget dimensions and mouse events are still interpreted as though they were at their original size. For example, the mouse move script function will always indicate a coordinate of (510,510) when you move the mouse into the lower-left corner of a 500x500 widget positioned at 10,10, no matter how big or small the GUI is scaled onscreen. Widget::GetWidth() will return the widget's unscaled dimensions, not the actual size it appears at. To get the actual size, you would multiply the dimensions by the GUI scale factor and round off. However, you should never need to calculate this yourself, as all numbers are made uniform for you.
     
    Scaled widget positions are still rounded to integers in order to make them appears precisely on the screen pixels. We don't want sub-pixel blurring with widgets that line up in between pixels, we want sharp clear pixel-aligned rendering. Interestingly, the mouse script functions are now receiving floating point coordinates instead of integers. For example, if you move the mouse across a widget with the GUI scaled to 200% (2.0) you will see the mouse coordinates change in 0.5 increments like 150, 150.5, 151, 151.5, 152, etc.
     
    Scaling presently does not affect text rendering, as this will take more work to figure out how to manage.
     
    Below is a simple GUI scaled at 100%:

     
    Now at 200%. Note that text scaling is not yet implemented. When it is, the text will appear bigger, proportional to the widget. However lines still appear exactly one pixel thick, as they should:

     
    And finally made smaller, at 50%: Notice that although the lines are still one pixel thick, we have lost the space between the inner and outer rectangles. This is being drawn properly, but it is possible you might run into small issues like this in various resolutions, so it's something to keep in mind.

     
    Of course images cannot be upsampled without losing precision, so any images your GUI uses should originally be at the maximum scale your GUI may use.
  13. Josh
    A big update for the beta of the upcoming Turbo Game Engine is now available, adding support for VR and Lua script!
    VR Rendering
    Turbo Game Engine now supports VR rendering, with support for true single-pass stereoscopic rendering on Nvidia GPUs. Other hardware will use a double-rendering path that is still faster than Leadwerks. To turn VR on simply call EnableVR(). Controllers are not yet supported, just rendering.
    Lua Scripting
    Lua script is now supported in Turbo! The amount of classes and functions is limited, but the foundation for full Lua support is in. Here are some of the great features in the new system.
    No Script Object
    All scripts operate on the entity itself. Instead of this:
    function Script:Update() self.entity:Turn(1,0,0) end You type this:
    function Object:Update() self:Turn(1,0,0) end This is especially nice when it comes to functions that retrieve another entity since you don't have to type "entity.script" to get Lua values and functions:
    function Object:Collision(entity,position,normal,speed) entity:Kill() end Smart Pointers
    Lua garbage collection now works together with C++11 smart pointers. You will never have to deal with invalid pointers or object deletion again. There is no Release() anymore. Just set your variable to nil to delete an object:
    local box = CreateBox(world) box = nil If you want the object to be collected immediately, you can force a GC step like this:
    local box = CreateBox(world) box = nil collectgarbage() Best of all, because Lua runs on the game logic thread separate from the rendering thread, it's perfectly fine to use Lua high-performance applications, even in VR. A pause in the game execution for Lua garbage collection will not pause the rendering thread.
    Multiple Scripts
    You can add any number of scripts to an object with the AddScript command:
    entity->AddScript("Scripts/Object/test.lua") Scripts on Any Object (Experimental)
    Now all object types can have scripts, not just entities:
    material->AddScript("Scripts/Object/Appearance/Pulse.lua") Set and Get Script Values in C++
    You can easily set and get values on any object, whether or not it has had a script added to it:
    entity->SetValue("health",100); Print(entity->GetNumberValue("health")); Vector Swizzle
    Using getters and setters I was able to implement vector swizzles. If you write shaders with GLSL you will be familiar with this convenient  feature:
    local a = Vec3(1,2,3) local b = a.xz --equivalent to Vec2(a.x,a.z) In Lua you can now return any combination of vector elements, using the XYZW or RGBA names. The code below will swap the red and blue elements of a color:
    local color = Vec4(1,0,0.5,1) local color = color.bgra Not only can you retrieve a value, but you can assign values using the swizzle:
    local v = Vec3(1,2,3) v.zy = Vec2(1,2) Print(v) --prints 1,2,1 Note there are presently only two engine script hooks that get called, Start() and Update().
    Simpler Uber Shaders
    I decided to do away with the complicated #ifdef macros in the shaders and use if statements within a single shader:
    //Diffuse Texture if (texturebound[0]) { color *= texture(texture0,texcoords0); } This makes internal shader management MUCH simpler because I don't have to load 64 variations of each shader. I am not sure yet, but I suspect modern GPUs will handle the branching logic with no performance penalty. It also means you can do things like have a material with just a color and normal map, with no need for a diffuse map. The shader will just adjust to whatever textures are present.
    A C++ Turbo program now looks like this:
    #include "Turbo.h" using namespace Turbo; int main(int argc, const char *argv[]) { //Create a window auto window = CreateWindow("MyGame", 0, 0, 1280, 720); //Create a rendering context auto context = CreateContext(window); //Create the world auto world = CreateWorld(); //This only affects reflections at this time world->SetSkybox("Models/Damaged Helmet/papermill.tex"); shared_ptr<Camera> camera; auto scene = LoadScene(world, "Maps/start.map"); //Create a camera if one was not found if (camera == nullptr) { camera = CreateCamera(world); camera->Move(0, 1, -2); } //Set background color camera->SetClearColor(0.15); //Enable camera free look and hide mouse camera->SetFreeLookMode(true); window->HideMouse(); while (window->KeyHit(KEY_ESCAPE) == false and window->Closed() == false) { //Camera movement if (window->KeyDown(KEY_A)) camera->Move(-0.1, 0, 0); if (window->KeyDown(KEY_D)) camera->Move(0.1, 0, 0); if (window->KeyDown(KEY_W)) camera->Move(0, 0, 0.1); if (window->KeyDown(KEY_S)) camera->Move(0, 0, -0.1); //Update the world world->Update(); //Render the world world->Render(context); } return 0; } If you would like to try out the new engine and give feedback during development, you can get access now for just $5 a month.
    I am now going to turn my attention to Leadwerks 4.6 and getting that ready for the Christmas season.
    The next step in Turbo development will probably be physics, because once that is working we will have a usable game engine.
  14. Josh
    I recently had a strange interest in looking at Lego sets online and watching reviews. There was something about them that I felt like related to game development and level design. Something very interesting I noticed was that each set would include several extra elements that formed a story line. This added "play potential" to the set. For example, the raft below includes three pirates loaded with weapons, one who is obviously the leader, a treasure map, so the crew has an objective, and a shark, which serves as an adversary or obstacle to overcome. This is a lot more interesting than if they had just made a simple raft and sold it on its own.
     

     
    Sets of 3D models tend to be sold as a collection of objects. You buy five cars, and hopefully you've got that car problem taken care of for the rest of your life. It's kind of dull and tends to become a chore of "collecting" things that don't add a lot of value to a game.
     
    Based on this idea, I have several DLCs planned that are made up of items that create unique gameplay opportunities. Each pack includes unique scripted items that don't exist anywhere else, and sets up new "play potential" for your games. This could also allow me to distribute the cost of character models across several packs. At $6000 / model I can't quickly recover the cost of production of a pack of AAA characters. If they were distributed in a DLC with environment props I can offset the cost of production and build new content in a sustainable manner. I hope to have some of these available before Christmas.
     
    We're also experimenting with building higher quality content. Game assets are still a huge problem. The fact is that most of the third-party content available on the web just isn't that good, or is very inconsistent. In fact the phrase "unity asset" has become a common derogatory term for "bad artwork", regardless of its origin. If making large collections of high-quality content was profitable, people would be doing it. So it seems to me this is a sales problem.
     
    The modding community tends to have a ton of high-quality original artwork, while the indie game development community tends to have terrible artwork and presentation, as a general thing. This has always confused me. I'm not talking about pre-made game content, I mean the work the community itself produces. Modders always have much better art and presentation. You can even see this in Vectronic, a game that began as a Source Engine mod. Why does indie game artwork have to be instantly recognizable and kind of amateurish? This needs to change.
     
    Fortunately, our DLC sales are quite high, higher than anything else on the web, from what I have heard talking to various artists. We can produce high-quality content and recoup the costs, even making a small profit. This has to be carefully planned though. Contracted work works best when you have a longstanding relationship with the contractor. It can be difficult to get things started initially.
     
    For environment art, I've relied on Ancient Idol Studio for several years. I've talked to Rich about the need to raise our art standards, and he thinks he can produce better output with a bigger budget. We're starting by modeling a new pickup truck as a test.
     

     
    His instructions are to produce something similar in quality to the vehicles in "SpinTires", with a 6000 poly budget:
     

     

     
    Eventually, we're going to revise the entire demo level. I'm starting slowly by focusing on one item so we can learn what we need to, establish our art style, and then ramp up production of more content.
     
    I also have someone working to replace our default pistol vwep. If this works out, I plan to rely on them to produce more weapons.
     
    I'm still looking for a freelancer or shop for character models. It's difficult to find someone with work that isn't overly stylized, is cost-effective, and good with communication. I'm willing to be flexible on costs, but spending more money doesn't necessarily mean you get a better result. This is made more difficult by the fact that most portfolios tends to show off the most outlandish art...which isn't a good representation of what I am looking for.
     
    My goal right now is to get one good environment model, one good vwep, and one good character, with a cost that can be quickly recovered. If we can get one of each, we can keep producing more and DLC sales will cover the costs.
  15. Josh
    I got the remaining glitches worked out, and the deal is that clustered forward rendering works great. It has more flexibility than deferred rendering and it performs a lot faster. This means we can use a better materials and lighting system, and at the same time have faster performance, which is great for VR especially. The video below shows a scene with 50 lights working with fast forward rendering
    One of the last things I added was switching from a fixed grid size of 16x16x16 to an arbitrary layout that can be set at any time. Right now I have it set to 16x8x64, but I will have to experiment to see what the optimum dimensions are.
    There are a lot of things to add (like shadows!) but I have zero concern about everything else working. The hard part is done, and I can see that this technique works great.
  16. Josh
    Leadwerks Engine 2.3 has been released. This major update includes a new editor, Lua script integration, forest rendering, roads, and more.
     
    Lua Script Integration: Write object scripts and write your game in any programming language, or you can write your entire game in Lua.
     
    New Editor: Leadwerks Editor is our new tool for creating enormous terrains, placing objects, and creating worlds. You can even edit scripts and see the results instantly.
     
    Forest Rendering: An advanced new rendering algorithm makes it possible to render hundreds of thousands of trees and plants, at playable framerates. Make dense forests that stretch for miles.
    Roads: Build spline-based roads and paths in the editor. Roads can branch, stretch, and fade out. You can even go for a drive on your highways and byways in the editor.
     
    Terrain Holes: Remove patches of terrain and go underground with caves and bunkers. This feature brings a new subterranean dimension to your games.
     
    High Dynamic Range (HDR): Brights are brighter, and darkness is darker. High dynamic range allows a greater range of color to be displayed, with automatic iris adjustment to correct for the ambient lighting environment.
     
    Animation Improvements: Characters can have up to 256 bones, with fast hardware skinning performed on the graphics card.
     
    Rendering Framework: It's easy to toggle advanced effects on and off, in any combination, with commands like SetBloom() and SetHDR().
     
    On Lua
    Like many programmers, I used to consider scripting languages to be a "toy": They are nice and easy to use, but ultimately I thought it was just programming for people who don't want to install Visual Studio. I started playing with Lua back in June of 2009, and the potential for this wonderful language quickly became apparent. Our integration of Lua allows script to be written on both a global and per-object basis; You can write your entire program in Lua, like you would with C++ or another language, or you can write a script specifically for one object class, with different predefined functions to be called during the program. There are functions that can be called when an object hits something, when it is created, or once every frame to update the object. Object scripts can be used together with C++ or another programming language. This means that anyone who uses Leadwerks Engine, with any language, can download a scripted object, drop it into their program, and have the same behavior working in their own game. The implications of this are immense.
     
    Suddenly we found that instead of being a collection of a few hundred individual programmers, the community suddenly became a collaborative, collective group. If one developer writes a script for a scary monster and posts it online, another user can download it, drop it into the editor, and see the results, with no code changes. You can even edit the script in Leadwerks Editor, save it, and instantly see the results. For the first time, developers could easily work together and share their creations, and still use whatever programming language they preferred. Lua has changed the whole feel of the community to a more social and cooperative group.
     




    Keeping it Real-time
    Our design philosophy for Leadwerks Engine is that everything should run in real-time, and thus be editable in real-time. Lighting and physics are already editable in real-time with instant feedback, but it took some time before I realized we can extend this approach to something else: programming. The new editor has an integrated script editor. To edit an object's script, you simply double-click on its node in the scene tree. You can view all the code that makes the object to whatever it is doing. You can also make changes to the script, save it, and the object's behavior is instantly updated! The lack of a compiling phase allows you to fine-tune behavior to get it just the way you want. Plus, it's a just lot of fun to use. For an in-depth description of our Lua implementation, see here:
    http://www.leadwerks.com/files/Tutorials/Lua/Getting_Started_With_Lua.pdf
     
    The Road to Perfection
    Roads look nice, but the reason I really like them is they allow new dimensions of gameplay. When I started off writing the road editor, I had a few requirements. I wanted the roads to be written entirely with Lua object scripts. This was a challenge, and forced me to add several features to the script integration to make it work. I also wanted roads that aligned perfectly to the terrain, like they do in Crysis. First I wrote the mesh generation routine that makes a spline-based road. This is fairly simple, but it is only part of the problem. The biggest challenge was slicing the road up by the terrain grid, so that every vertex on the road lined up along the terrain, and the whole road lay perfectly snug. UV mapping the tessellated road was another major challenge, but a clever solution was found after many attempts. The resulting roads look great and conform perfectly to the terrain. And they're fun to go driving on!
     
    Seeing the Forest for the Trees
    The forest rendering algorithm is my favorite new rendering feature. The bulk of this was written in the spring of last year, but there were a few details I didn't complete until recently. I spent hours playing with the Crysis editor and trying to figure out how they heck they were able to render so much foliage. I read article after article, but no one seemed to have a good approach. Most of the forum discussions on graphics development sites seemed to amount to just saying "use billboards". The problem is that rendering 100,000 billboards is still a major challenge. The solution was found by analyzing what format data the GPU will render most efficiently, and then working backwards to find a way to get the vegetation in that format. The solution is elegant, and seamless to the end user. There's no baking or pre-processing. You paint as many trees and plants on the terrain as you want, and it just works. I still have some more ideas to implement, like destructable trees and even better efficiency, but I am quite happy with how it turned out.
  17. Josh
    Since I am working with the Blender import pipeline and also getting artists onboard the Steam Workshop, it makes sense to work out any little details in the art pipeline that can be improved. One of these is the texture import process.
     
    Leadwerks 3.0 was designed to function across PC and mobile, and one of the compromises that had to be made was texture compression. There is no universally supported texture compression format on mobile, while the PC has several variations of DXT compression to choose from. It is possible to save textures with DXT compression and then uncompress them on mobile devices, but then you end up increasing your load times. OpenGL 4.4 finally has a universal compression format, but none of the hardware out today supports it. So there's not really a good solution here and this is an example of how mobile was holding back what we could do with the engine.
     
    I've just added controls in the texture editor to select the DXT compression level used. The default is DXT1. The Leadwerks 2 DXT1 compressor had some errors in it that would cause visual artifacts, and the format gets a bad rap overall, but where else can you get 80% of the quality in 12% the video memory? Nowhere, that's where! I fixed the compression errors in my code and I can't tell a difference between most uncompressed textures and the DXT1 version in Leadwerks 3.1. You can also select the compression format, save the texture and see for yourself what you want to use. DXT3, DXT5, and DXT5n are also available.
     
    DXT5n is an interesting format, because it's just DXT5 with the red and alpha channels swapped. In DXT5 compression, the alpha channel has a higher resolution than the red channel, so using this format for normal maps can compress your normal maps while avoiding the visual artifacts texture compression can cause when used with normal maps. If your texture is checked as a normal map and any compressed format is selected, the texture converter will automatically override that selection with DXT6n, in order to prevent mistakes. OpenGL 4 allows a texture swizzle setting so I can set it so a shader will automatically swap and red and alpha channels during a texture lookup. This is another reason I want to focus on modern OpenGL; a fallback renderer would not be able to read these textures correctly, which means normal maps could never use compression.
     

     
    Finally, an unsharp mask filter has been added. Textures work by storing many downsampled copies of the texture all the way down to a 1x1 image. This is why textures ideally need to be power-of-two sized. However, that downsampling process can make images appear blurry at a distance. The new unsharp mask filter will perform a sharpen operation on each level of the mipmap chain. As you can see in the image below, this can retain details that would otherwise be lost. In the shots below I am zooming out of the texture so you can see the lower-resolution mipmaps.
     
    Here is the image without sharpening:

     
    And with:

     
    These additions can be gotten first by opting into the beta branch on Steam. Once a stable build is reached, a full update will be performed on the standalone build and on the default branch on Steam.
  18. Josh
    Last week I travelled to Seattle for a few days to lay some groundwork for the future of Leadwerks Software. During the week I tried out Valve's VR hardware, visited a gassification plant, and survived a trifecta of astrological phenomenon.

    Steam VR
    Although I attended Steam Dev Days, I did not get a chance to demo Valve's virtual reality hardware. I was invited by one employee to come back and check it out, so I decided to take them up on that offer last week. 
    Valve's VR is improved over the Oculus Rift because it has higher resolution screens, lower latency, and positional tracking. The combination of these factors are enough to push the stimulus over the tipping point to where your brain accepts it as reality. Donning the headset immediately transported me to another plane of existence. If Oculus Rift felt like looking through a Viewmaster, Steam VR was like plugging into the matrix. In fact, it was so real that it felt very alarming to look down and not have a body. I was led through a series of demos that were incredible and disturbing at the same time. At one point I was looking in a mirror at a levitating box with two hemispheres for eyes that mimicked the rotation of my head. The sense of disembodiment was alarming in a very visceral way. I never experienced anything like this with Facebook's less convincing VR technology. The Oculus Rift was interesting, but never raised existential questions in my mind.
     
    Positional tracking inserts you into the scene and gives you a sense of space relative to objects. Objects in the foreground are especially real because you can move around them and see them up close from all angles. 3D modeling for virtual reality is going to be different from modeling for conventional 3D games. Small details need to be actual geometry, not just details baked into a normal map, or else they look like painted plywood. Because of the sense of space, small enclosed areas actually make a better setting for VR than large expansive fields, where your sense of position is diminished.
     
    At one point I was face to face with a massive 20-foot tall model of a robot from Portal 2, and I felt the size of it in a way I simply wouldn't from an image onscreen. The positional tracking made the scale seem absolutely real. I stared up at it from below, and I knew at that point that VR is going to be huge. I'm still convinced I was actually there. I have the memory of that experience. It was just as real as the room I'm in now as I type this.


    Gas Works
    In college I took a trip to visit some friends who moved to Seattle. (They moved there because they thought it would be fun, and we were all Nirvana / Soundgarden / Pearl Jam fans in high school.) That was my first visit to the emerald city. While there, we visited Gas Works park, the former site of a "gassification plant" where I presume they manufactured steampunk memorabilia:
     
    I was inspired by the heavy industrial structures and the name stuck with me, and it influenced the naming of Leadwerks Software. I'm starting to feel like the Joker with my explanations of where the name comes from ("Did I ever tell you how I got these scars?...") but the fact is that I remembered the park but forgot the name until I saw it on a poster last week. And then I had come full circle. Gas Works. Leadwerks. Seattle. It all made sense, and I knew this is where the company belongs.
     
    The Seattle area is home to Amazon, Microsoft, Valve, and 200 game studios. Not only are video games a major area industry, but PC gaming is very strong here. As a result, I don't have to explain to people what Steam is. I don't have to explain to them what a mod is. I attended a TechCrunch event Thursday night and my suspicions were confirmed. Conversations were easy and everyone knew right away what I was talking about. The opportunities that fit our particular strengths are to be found here. As of July, the company is relocating to Seattle.

    Friday the 13th and the Strawberry Moon
    My flight departed Seattle at 1:40 P.M., on what happened to be Friday the 13th. After an uneventful boarding we sat on the tarmac waiting to take off, when the captain announced the plane was low on hydraulic fluid. A few minutes passed and they announced the maintenance crew was checking a problem, and they would let us know if we were cleared to take off. They then announced we were returning to the boarding area and would be delayed. At this time I decided to adopt a new policy: I don't fly on planes that have known mechanical issues. I requested to reschedule my flight and took the next one home. My original flight did depart and arrive safely, as I figured it would, but in the face of incomplete information I'm not one to take stupid chances. Apparently Friday was also a full "strawberry moon" and a guy who worked at a coffee shop in the airport told me that Jupiter is doing some weird stuff. He was a barista after all, so I figured I should listen to him. I arrived safely that night on another flight. A strange end to a pivotal week.
  19. Josh
    Now that I have all the Vulkan knowledge I need, and most work is being done with GLSL shader code, development is moving faster. Before starting voxel ray tracing, another hard problem, I decided to work one some *relatively* easier things for a few days. I want tessellation to be an every day feature in the new engine, so I decided to work out a useful implementation of it.
    While there are a ton of examples out there showing how to split a triangle up into smaller triangles, useful discussion and techniques of in-game tessellation is much more rare. I think this is because there are several problems to solve before this technical feature can really be made practical.
    Tessellation Level
    The first problem is deciding how much to tessellate an object. Tessellation should use a single detail level per set of primitives being drawn. The reason for this is that cracks will appear when you apply displacement if you try to use a different tessellation level for each polygon. I solved this with some per-mesh setting for tessellation parameters.
    Note: In Leadwerks Game Engine, a model is an entity with one or more surfaces. Each surface has a vertex array, indice array, and a material. In Turbo Game Engine, a model contains one of more LODs, and each LOD can have one or more meshes. A mesh object in Turbo is like a surface object in Leadwerks.
    We are not used to per-mesh settings. In fact, the material is the only parameter meshes contained other than vertex or indice data. But for tessellation parameters, that is exactly what we need, because the density of the mesh polygons gives us an idea of how detailed the tessellation should be. This command has been added:
    void Mesh::SetTessellation(const float detail, const float nearrange, const float farrange) Here are what the parameters do:
    detail: the higher the detail, the more the polygons are split up. Default is 16. nearrange: the distance below which tessellation stops increasing. Default is 1.0 meters. farrange: the distance below which tessellation starts increasing. Default is 20.0 meters. This command gives you the ability to set properties that will give a roughly equal distribution of polygons in screen space. For convenience, a similar command has been added to the model class, which will apply the settings to all meshes in the model.
    Surface Displacement
    Another problem is culling offscreen polygons so the GPU doesn't have to process millions of extra vertices. I solved this by testing if all control vertices lie outside one edge of the camera frustum. (This is not the same as testing if any control point is inside the camera frustum, as I have seen suggested elsewhere. The latter will cause problems because it is still possible for a triangle to be visible even if all its corners are outside the view.) To account for displacement, I also tested the same vertices with maximum displacement applied.
    To control the amount of displacement, a scale property has been added to the displacementTexture object scheme:
    "displacementTexture": { "file": "./harshbricks-height5-16.png", "scale": 0.05 } A Boolean value called "tessellation" has also been added to the JSON material scheme. This will tell the engine to choose a shader that uses tessellation, so you don't need to explicitly specify a shader file. Shadows will also be rendered with tessellation, unless you explicitly choose a different shadow shader.
    Here is the result:

    Surface displacement takes the object scale into account, so if you scale up an object the displacement will increase accordingly.
    Surface Curvature
    I also added an implementation of the PN Triangles technique. This treats a triangle as control points for a Bezier curve and projects tessellated curved surfaces outwards.
     
    You can see below the shot using the PN Triangles technique eliminates the pointy edges of the sphere.


    The effects is good, although it is more computationally expensive, and if a strong displacement map is in use, you can't really see a difference. Since vertex positions are being changed but texture coordinates are still using the same interpolation function, it can make texture coordinates appear distorted. To counter this, texture coordinates would need to be recalculated from the new vertex positions.
    EDIT:
    I found a better algorithm that doesn't produce the texcoord errors seen above.


    Finally, a global tessellation factor has been added that lets you scale the effect for different hardware levels:
    void SetTessellationDetail(const float detail) The default setting is 1.0, so you can use this to scale up or down the detail any way you like.
  20. Josh
    I created a test of 1000 animated crawler models to see how the performance of Vulkan stacks up against our older OpenGL renderer. Here it is in OpenGL running at 37 FPS, which is pretty respectable considering how many animations are playing (and about 4 million polygons).

    With Vulkan the same test yields a framerate of 66 FPS, 78% faster than the OpenGL version.

    Here is a video of the characters animating. Each skeleton is its own unique animation system, there are no shared skeletons in this demo.
     
  21. Josh
    This is just so cool. You can generate simple primitives for any entity. A convenient "Fit Shape" button will wrap the primitive around the entity, with an option to include the entity's children. However, the real power comes when you generate physics shapes from models. You can right-click on any model and get a menu to generate a physics shape. The polygon mesh and convex hull will look familiar if you've used the PhyGen tool in Leadwerks Engine 2. There's also a new option called "Convex Decomposition".

     
    Convex decomposition is an advanced algorithm that takes a polygonal mesh and outputs a series of convex shapes. This is helpful because real physics calculations require geometry with actual volume instead of just being a polygon soup. Convex decomposition can turn any polygonal mesh into a physics shape, such as the Stanford bunny shown below:

     
    This means you can generate accurate physics shapes for any model, without creating special physics models in 3ds Max or Blender.
     
    Take complex objects like this Utah teapot and generate a physics shape for it, then attach that shape to the model and save it as a prefab. Here you can see the physics shape that was calculated for the teapot using convex decomposition. Navigation is turned on to make the teapot into a dynamic obstacle AI will avoid going through:

     
    It's taken a long time to build a game development platform that accelerates development without restricting you, but I think the results are worth it. I hope you are looking forward to using these tools as much as I am.
  22. Josh
    A new map called "09-Change Map.map" has been added to the example game. This demonstrates how to use the new TriggerChangeMap.lua script to make your player progress from one level to the next.
     
    In order to use this script, you must update your project with the new executables. Select the File > Project Manager menu to open the Project Manager, then press the "Update" button to update your project.
     
    A new chunk of code has been added to App.lua to handle map changes. This code will be found in the main loop of the new App.lua script:

    --Handle map change if changemapname~=nil then
     
    --Clear all entities
    self.world:Clear()
     
    --Load the next map
    Time:Pause()
    if Map:Load("Maps/"..changemapname..".map")==false then return false end
    collectgarbage()
    Time:Resume()
     
    changemapname = nil
    end
     
    Using the TriggerChangeMap script is easy. Just attach it to a CSG brush and set the name of the next map you want to load, without the path or file extension. For example, to load the map "Maps/09-Change Map.map" you just type in "09-Change Map" in the "Map Name" field. When the player collides with the brush, the new map will be loaded. You can make the brush a panel on the floor, or an invisible trigger field.
     
    You must opt into the beta branch on Steam to get these changes.
  23. Josh
    I've begun implementing unicode in Leadwerks Game Engine 5.  It's not quite as simple as "switch all string variables to another data type".
    First, I will give you a simple explanation of what unicode is.  I am not an expert so feel free to make any corrections in the comments below.
    When computers first started drawing text we used a single byte for each character.  One byte can describe 256 different values and the English language only has 26 letters, 10 numbers, and a few other characters for punctuation so all was well.  No one cared or thought about supporting other languages like German with funny umlauts or the thousands of characters in the Chinese language.
    Then some people who were too smart for their own good invented a really complicated system called unicode.  Unicode allows characters beyond the 256 character limit of a byte because it can use more than one byte per character.  But unicode doesn't really store a letter, because that would be too easy.  Instead it stores a "code point" which is an abstract concept.  Unfortunately the people who originally invented unicode were locked away in a mental asylum where they remain to this day, so no one in the real world actually understands what a code point is.
    There are several kinds of unicode but the one favored by nerds who don't write software is UTF-8.  UTF-8 uses just one byte per character, unless it uses two, or sometimes four.  Because each character can be a different length there is no way to quickly get a single letter of a string.  It would be like trying to get a single byte of a compressed zip file; you have to decompress the entire file to read a byte at a certain position.  This means that commands like Replace(), Mid(), Upper(), and basically any other string manipulation commands simply will not work with UTF-8 strings.
    Nonetheless, some people still promote UTF-8 religiously because they think it sounds cool and they don't actually write software.  There is even a UTF-8 Everywhere Manifesto.  You know who else had a manifesto?  This guy, that's who:

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

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

    So in conclusion, unicode will be used in Leadwerks and will work for the most part without you needing to know or do anything different, allowing games you develop (and Leadwerks itself) to work correctly on computers all across the world.
  24. Josh
    I have shadow caching working now in Turbo. This feature is already in Leadwerks Game Engine 4. The idea is that static scene geometry should not be redrawn when a dynamic object moves. Imagine a character (6000 polys) walking across a highly detailed room (100,000 polys), with one point light in the room. If we mark the scene geometry as static and the character as dynamic, then we can render a shadow map cache of the static scene once. When the character moves, the static cache is copied into the rendering buffer, and then the character is drawn on top of that, instead of re-rendering the entire scene. When used correctly, this will make a huge difference in the amount of geometry the renderer has to draw to update lighting.
    Here is my test. The helmet spins, causing the point light shadow to re-draw. The surrounding scene is marked as static and consists of 260,000 polys. By using shadow caching we can reduce scene polys rendered from about 540,000 to 280,000, in this case.

    I actually changed the light type to a point light, which is more demanding since it uses six passes to oover all faces of the cubemap. After performing optimizations, the test runs at 180 FPS, with a point light, with shadow caching enabled. Without shadow caching it ran at about 118. This is with Intel integrated graphics, so a discrete card is sure to be much faster.
    I also found that using variance shadow maps and multisampled shadows DO make a big difference in performance on Intel graphics (about half the framerate with 4X MSAA VSMs), but I don't think they will make any difference on a high-end card.
    There is still a bit of an issue with shadow updates syncing with the rendering thread, but all in all it was a good day's work.
     
  25. Josh
    When I saw specs for the graphics card they are using in the new iMacs, I knew it was time for Leadwerks to come to Mac. Steam for Mac also recently came out, so the time seems right for Mac gaming. Of course, some guy blew up the Roseville Galleria, so the Apple store was closed. I ordered the 27" iMac with a 3.2 ghz dual core processor and upgraded the graphics card to an ATI 5750. Here's what they sent me:

    The computer case/monitor (it's all one piece) is a solid piece of aluminum that doesn't bend or crackle when you pick it up, like most PC equipment. It's very nice to have a solid geometric shape with such stability.
     
    The keyboard looks weird at first, but I got used to typing on the low flat keys very quickly. The mouse has a touch surface instead of a mouse wheel, which works well for scrolling pages, but doesn't work at all for switching weapons in a game. The shape of the mouse is not very ergonomic, and I don't think I will be able to use it for extended periods of time unless I figure out a better way to hold it. The mouse and keyboard are both wireless. In fact, I have a computer with speakers, mouse, keyboard, camera, and internet, and only one cord is coming out of it, for power. It definitely cuts down on space and mess.
     
    The monitor is the most beautiful I have ever seen. It's 27 inches with a native resolution of 2560x1440. At that resolution, you can't even see the individual pixels. The desktop background looks like a photograph because you can't see pixels. I have a black and white photo in the background and I can see individual grains of sand on a beach. It's really incredible. Unfortunately, the OS has not adjusted to this fine resolution. There is no way to adjust the font size for the whole interface, so if you have the monitor set at its native resolution you will be looking at text that would be about a size 6 or 7 on a 1920x1080 monitor of the same physical size. You can lower the resolution, but then text gets slightly blurred. A few programs like Xcode and Safari let you adjust some of the text sizes, so that is what I am getting by with for now. This giant image is squeezed onto a 27" monitor:

    The speakers embedded in the monitor (that I didn't even know existed) are surprisingly good. I sort of got used to my bass-heavy Altec Lansing speakers with bad shielding and a constant buzz. It's nice to have good clear speakers and no subwoofer to trip over.
     
    The ATI 5750 has 720 stream processors (equivalent to about 144 NVidia stream processors) but I was apprehensive about performance because it is a mobile card. Source engine games ran great with max settings at the monitor's native resolution of 2560x1440. Remember, this is running in native OSX, not Windows with boot camp:

    I wanted to see how a more demanding engine would do, so I installed Windows 7 on a separate partition and ran Crysis. I was surprised to see the game running with max settings at about 18 frames per second, at the monitor's massive resolution. Of course, lowering the screen resolution improved performance. So that was not bad at all.

    I spent some time with the Mac developer tool Xcode. It's about the same as Visual Studio. It uses GCC to compile, which cuts the ten minute build time in Visual Studio down to about a minute. I could get the same results on Windows using Code::Blocks so that's not really a Mac thing, but it's nice to work with. I am pleased to say that Leadwerks Engine 3 is now compiling for Windows and MacOS. The Mac version has no graphics or GUI yet, but it compiles without any errors, and that is a big achievement. Linux should be easy now, since it's a lot more like Mac than Mac is like Windows. I also tried out the iPhone SDK and was pleased to see the iPhone emulator starts up almost instantly. By contrast, the Android SDK took me about 45 minutes to install the first time, and it took ten minutes for the emulator to start.
     
    So my conclusion on Mac is that I am very pleased with just about every aspect of them. You could reasonably get away with only having an iMac for development, because it allows you to make games for Windows, Mac, Linux, Android, and iPhone. I may make MacOS my main operating system, although I have to keep some other machines around for compatibility testing. It's taking some extra time to get the cross-platform support for Leadwerks Engine 3 in from the beginning, but I think a couple extra weeks spent now will be well worth it in the long run. Soon enough the code will be platform-agnostic, meaning I can just work with my own code, instead of trying to figure out somebody's else's designs.
     
    I picked up Left 4 Dead 1 and 2 last week in a Steam Halloween sale. I'm starting with the first one, and I absolutely love it. I've always liked co-op play since Duke Nukem, and there hasn't been nearly enough of it available. I also love zombie movies, so Left 4 Dead is right up my alley.


×
×
  • Create New...