Jump to content

Josh

Staff
  • Posts

    23,218
  • Joined

  • Last visited

Blog Entries posted by Josh

  1. Josh
    Step 1. Build the object you want to make into a prefab. Here I have staircase I want to make into a reusable object.

     
    Step 2. Drag all the objects onto one, to make it a parent of the others. Prefabs need one top-level entity.

     
    Step 3. Click on the top-most object in the hierarchy and select the "Save as Prefab" menu item. You'll get a file save dialog so you can choose the file to save.

     
    Step 4. Begin using your prefab by dragging it into the scene from the asset browser.

     
    Prefabs will always be reloaded from the original prefab file. Even if you overwrite the prefab file, your changes will be instantly reflected throughout the entire scene. If you make any changes to an object loaded from a prefab that would make that object so it is no longer instanced, a dialog box will warn you and ask if you want to proceed before turning it into a unique object.
     
    Prefabs can be made from all entities in the editor, not just CSG brushes.
  2. Josh
    I've always believed strongly that a native UI presents a cleaner, more consistent, and more beautiful interface for the user to interact with. Not only do they look better, but they present less of a learning curve because the user already knows how to interact with them. When applications move beyond competing to see who can make a better home-made button, they can focus on greater underlying functionality and stop wasting time reinventing the wheel that the operating system already provides.
     
    Leadwerks 3 uses the native UI on both Windows and OSX, and it's turning out really beautiful. Since one picture = 1000 words, I'm going to let the images do the rest of the talking.
     

     

     

     

     

     

  3. Josh
    The Sacramento Hacker Lab is a new facility of offices and shared workspace for tech startups and developers to work. They threw a party last night and Chris and I went.
     
    I saw a 3D printer for the first time:

     
    And I got to hold a printed replica of Admiral Ackbar's head. This lead to me randomly yelling out "IT'S A TRAP!" for the remainder of the evening:

     
    The major players in the Sacramento game industry plotting to take over the world:

     
    I think these 4x4's turn into robots, or something:

     
    More people in the front!

     
    Some office are still WIP. Beer makes the work go faster:

     
    A scene from one of the more hipsterish offices:

     
    No drinking until 5. (All the digits are fives):

  4. Josh
    I've been working with "Klepto2" to integrate the Scintilla text control into the new Leadwerks editor. Scintilla is very powerful and feature-rich, but is somewhat insane to work with. You've got to have a Scintilla expert onboard to integrate it successfully, which we fortunately do.
     
    Previously, I was relying on calls to a Debug:Stop() function to control breakpoints. This was hard-coded into your script program, and breakpoints could not be dynamically added or removed. Since Scintilla gives us the ability to edit breakpoints in the editor, I rewrote the debugging interface to give control to provide more communication between the application and the debugger. Breakpoints can now be added and removed as the program is running. You can also pause the program at any time from the editor and see where the code is executing.
     
    The script editor includes a great debugger that lets you view the entire contents of the Lua virtual machine. As a finishing touch, I added in some icons from Microsoft's Visual Studio icon pack. As a programmer, the result is something I really like working with:

     
    We think having a professional-grade code IDE integrated in the editor will provide a smoother and more seamless user experience, so you don't have to switch back and forth with third-party IDEs.
  5. Josh
    Step 1. Add your image files into the project directory. I just added a bunch of TGA files, and they show up in the editor right away:

     
    Step 2. Right-click on a texture and select the "Generate Material" menu item.

     
    Step 3. There is no step 3. Your material is ready to use. The normal map was automatically detected and added to the material.

     
    Here it is in the material editor.

  6. Josh
    I'm really shocked by how fast C++ can be. iOS and Android do not support GPU skinning, so I had to implement vertex-weighted skinning on the CPU. It took about a day to get running, and then I started optimizing code.
     
    My test case was an 8400 polygon model. Each vertex could be attached to as many as four bones, but most just used two or three bones. To make it more interesting, I put the vertex weighting code inside a loop so it would be performed ten times instead of once.
     
    When I started, the process took 23 milliseconds. I replace OO math code with procedural (including some inline functions), reduce the number of dynamically allocated objects, and make looping code as small as possible.
     
    One interesting thing I did was merging four float variables in a loop into a single float array. Instead of resetting each variable to 0 in each iteration of the loop, I did a single memcpy() from an array of zeroes I created just for this purpose.
     
    Before:

    position[0]=0; position[1]=0; position[2]=0; sumweights=0;
     
    After:

    memcpy(position,nullarray,16);
     
    This actually resulted in a very big speed increase!
     
    The Result
    By the time I was done, my stress test was executing in 4-5 milliseconds. The program now renders an 8400 skinned model at 1000 FPS, which I thought was impossible with CPU skinning.
     
    We're still going to implement GPU skinning in our high-end PC renderer, but the CPU skinning will be good for mobile devices and older hardware.
     

  7. Josh
    This year, Halloween makes an early arrival. Either that, or Josh is working on animation!
     
    Obviously this is wrong, but it's only a few lines of code away from being right, and I'll figure it out.
     
    Skinning in the new game engine is done on the CPU. You don't have to assign a special shader to make an animated model appear, but it is slower than GPU skinning. I can distribute the load across CPU cores, but no matter what we're not going to have free skinning like we do with Leadwerks Engine 2. Unfortunately, there is no cross-platform technique to get the bone matrices to the GPU in OpenGL2/OpenGLES2. Heck, I could barely get it working on NVidia and ATI cards on Windows.
     
    OpenGL 3+ provides a technique for handling this that is built into the specification. The vertex skinning routines are being designed for GPU skinning, so that it will be easy to adapt to OpenGL 3-4 in the future.
     
    Ha, this one's even better:

  8. Josh
    Scripting support is very important in the new engine. The script implementation in the old engine was great for quickly adding animation and simple behavior to objects, but wasn't capable of supporting advanced gameplay. I've put a lot of work into our implementation of Lua script to bring it to a new level.
     
    In the old engine, if a script error occurred the program would simply crash. The new engine supports detailed error handling and will automatically open files and show you where an error occurs in a script:

     
    You can insert breakpoints in your program with the Debug:Stop() command. When a breakpoint is hit, the program pauses. You can browse all variables running in the virtual machine in the script debug pane. You can even see actual memory addresses of C++ objects in the Lua virtual machine!:

     
    Of course, you can also step through your script line by line with code stepping:

     
    You can modify object scripts, and when you hit F5 the current scene will be saved and loaded in your project. The editor launches the game as an external executable, you don't have to worry about messing up your editing session, and you can even debug the virtual machine in C++ programs. This makes the editor stable and solid, and game iteration is still very fast.

     
    By focusing on improved Lua support and really digging into the details of the language, I hope we can provide a scripting environment that is very capable and pleasant to use.
  9. Josh
    Here's the layout for pure Lua applications. This will run on Android, iOS, Windows, and Mac, with no third-party compilers to mess around with. And because it's JIT compiled by the engine itself, it's fast:

    --This function will be called once when the program starts function App:Start()
     
    --Set the application title
    self.title="Darkness Awaits"
     
    --Create settings table and add defaults
    self.settings={}
    self.settings.vsync=true
     
    --Load the user's settings
    self:LoadSettings()
     
    --Create a window
    self.window=Window:Create(self.title,0,0,1024,768,Window.Titlebar)
     
    --Create the graphics context
    self.context=Context:Create(self.window,0)
    if self.context==nil then return false end
     
    --Create a world
    self.world=World:Create()
     
    --Create a camera
    self.camera = Camera:Create()
    self.camera:SetPosition(0,5,-10)
     
    --Load a test scene
    Scene:Load("Scenes\\trash.scene")
     
    return true
    end
     
    --This is our main program loop and will be called continuously until the program ends
    function App:Continue()
     
    --If window has been closed, end the program
    if self.window:Closed() then return false end
     
    --Update the world
    self.world:Update()
     
    --Render the world
    self.world:Render()
     
    --Refresh the screen
    self.context:Swap(self.settings.vsync)
     
    --Returning true tells the main program to keep looping
    return true
    end
     
    function App:Pause()
    --Pause time updating
    Time:Pause()
    end
     
    function App:Resume()
    --Resume time updating
    Time:Resume()
    end
     
    function App:Finish()
    --Save user settings to a file
    self:SaveSettings()
    end
     
    --Load user settings from a file
    function App:LoadSettings()
    return true
    end
     
    --Save user settings to a file
    function App:SaveSettings()
    return true
    end
     
    And just for fun, here's the C++ code that makes all the calls to Lua scripts. It's more low-level than you will need to deal with, but it shows how powerful the script command set is:

    bool App::Start() { Int stacksize = Interpreter::GetStackSize(); //Create New table And assign it To the Global variable "App" Interpreter::NewTable(); Interpreter::SetGlobal("App"); //Invoke the start script If (!Interpreter::ExecuteFile("Scripts\\App.lua")) { Print("Error: Failed to execute\"Scripts\\App.lua\"."); Return False; } //Restore the stack size Interpreter::SetStackSize(stacksize); //Call the App:Start() Function Interpreter::GetGlobal("App"); If (Interpreter::IsTable()) { Interpreter::PushString("Start"); Interpreter::GetTable(); If (Interpreter::IsFunction()) { Interpreter::PushValue(-2);//Push the app table onto the stack as "self" If (!Interpreter::Invoke(1,1)) Return False; If (Interpreter::IsBool()) { If (!Interpreter::ToBool()) Return False; } Else { Return False; } } } //Restore the stack size Interpreter::SetStackSize(stacksize); Return True; } bool App::Continue() { //Get the stack size Int stacksize = Interpreter::GetStackSize(); //Call the App:Start() Function Interpreter::GetGlobal("App"); If (Interpreter::IsTable()) { Interpreter::PushString("Continue"); Interpreter::GetTable(); If (Interpreter::IsFunction()) { Interpreter::PushValue(-2);//Push the app table onto the stack as "self" If (!Interpreter::Invoke(1,1)) { Print("Error: Script function App:Continue() was not successfully invoked."); Return False; } If (Interpreter::IsBool()) { If (!Interpreter::ToBool()) { Return False; } } Else { Return False; } } Else { Print("Error: App:Continue() function not found."); Return False; } } Else { Print("Error: App table not found."); Return False; } //Restore the stack size Interpreter::SetStackSize(stacksize); Return True; } void App::Pause() { //Get the stack size Int stacksize = Interpreter::GetStackSize(); //Call the App:Start() Function Interpreter::GetGlobal("App"); If (Interpreter::IsTable()) { Interpreter::PushString("Pause"); Interpreter::GetTable(); If (Interpreter::IsFunction()) { Interpreter::PushValue(-2);//Push the app table onto the stack as "self" If (!Interpreter::Invoke(1)) { Print("Error: Failed to invoke script function App:Pause()"); Return; } } } //Restore the stack size Interpreter::SetStackSize(stacksize); } void App::Finish() { //Get the stack size Int stacksize = Interpreter::GetStackSize(); //Call the App:Finish() Function Interpreter::GetGlobal("App"); If (Interpreter::IsTable()) { Interpreter::PushString("Finish"); Interpreter::GetTable(); If (Interpreter::IsFunction()) { Interpreter::PushValue(-2);//Push the app table onto the stack as "self" If (!Interpreter::Invoke(1)) { Print("Error: Failed to invoke script function App:Finish()"); Return; } } } //Restore the stack size Interpreter::SetStackSize(stacksize); }
  10. Josh
    We're getting to a point where we need to establish the final directory structure for the new game engine, and start using it. Here's what you see when you open "C:\Leadwerks" (or on Mac, "\Applications\Leadwerks"):

     
    The "Editor" folder contains the editor executable and support files.
     
    The "Engine" folder contains header files, compiles libraries, and BlitzMax include files.
     
    The "Help" directory contains local help files.
     
    The "Projects" directory is where your projects go, by default. Our development project is called "Darkness Awaits". Here's what the directory structure looks like:

     
    We think this design will keep things clean and organized, and be nice to work with.
  11. Josh
    Physics works great with scaled entities:

     
    For joints in the new engine, I am considering something fairly radical. In Leadwerks Engine 2, joints were just a class that wasn't an extension of the entity class. I was going to make a new Joint class that extends the Entity class. The problem is you would end up inserting a ton of intermediary joint entities into your model hierarchy, which would hurt the editor's ability to reload models on the fly.
     
    What if joints were just a feature in the Entity class? You could set an entity's joint mode and that would create a joint between itself and its parent. You could go through the hierarchy of a character and easily set up ball and hinge joints within the model hierarchy to make a rag doll:

     
    Of course this means no entity can be the child of two joints. Joint hierarchies would have to go one-way. For example, if you had a connection like this:
     
    [Parent] - Joint - [Child] - Joint - [Parent]
     
    It would have to be arranged like this instead:
     
    [Parent] - Joint - [Child] - Joint - [Child of Child]
     
    For example, in the picture below #1 is the top-level parent, and the children are numbered as they descend. If the blocks are each connected with joints, you will run into problems with the fifth level down, because you are trying to connect the child to two different parents:

     
    Are people really going to be making suspension bridges that disconnect and reconnect multiple times? Not sure yet if this would pose a problem or not. However, it would be super convenient to be able to load up that buggy model and add joints to the wheels and turret, without having to create and position new joint entities. It totally eliminates the problem of having to figure out which entities are attached to which joint.
     
    --EDIT--
    Hmmmm, maybe this isn't such a hot idea. Here's a situation where the design would totally fail:

     
    Since our physics are the only ones stable enough for robotics and scientific simulations, I think it needs to be able to accommodate complex machinery.
  12. 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.
  13. Josh
    AI is always a fun programming topic, and it's even more fun when you're mixing a physics-based character controller with dynamic navmesh pathfinding.
     
    We planned on using navmesh pathfinding from the very start of the design of the new engine. Because it was integrated from the very beginning our implementation works really nicely. Pathfinding is completely automatic and dynamic. There are no commands you need to call to make it work. When part of the scene changes, the navigation data for that sector is automatically recalculated. If you haven't seen it already, you can check out some videos on the Leadwerks YouTube channel:


     
    Character controllers are a special physics body used to control the movement of characters in the game. They are used for both players as a control method, and by enemies and NPCs. Sometimes they will be controlled by keyboard or touch input, and sometimes they are controlled by AI navigation. Consequently, the character controller class has to be able to handle both.
     
    There are so many cool things I can do that it's fun and a little scary. Right now I am toying with the following API. The first two commands would just make the character constantly move to the position or follow the entity you specify, so there's only need to call them once, and the engine will handle the rest:

    bool CharacterController::GoToPoint(const float& x, const float& y, const float& z) bool CharacterController::Follow(Entity* entity) void CharacterController::Stop()
     
    And then you still have manual controls, which are analogous to "UpdateController" from LE2:

    CharacterController::SetInput(const float& angle, const float& movement, const float& strafe...)
     
    In the screenshot below, I control the rightmost character with the keyboard, while the others are programmed to follow me:

  14. Josh
    The built-in level design tools in Leadwerks3D are great for quickly sketching out a game level. Most of this remains unchanged from the design of 3D World Studio, with a few extras like smooth groups and new primitive types:

     
    When a point entity object type is selected in the side panel, the object creation widget changes to a simple cross hair:

     
    The selection box with tabs that CSG brushes use are great for quickly moving, scaling, rotating, and shearing brushes, but they aren't that great for point entities. That's why I'm implementing a different control style for selected point entities. A 3D widget like 3DS Max uses will appear in the 3D and 2D viewports:

     
    To give the user more control, they can choose between global and local coordinate systems to move and rotate entities:

     
    We think this will provide the best combination of fast level editing with CSG brushes, and quick object manipulation for other types of objects.
     
    --EDIT--
     
    Here's an updated view of the 3D control widget. Of all the implementations of this I have seen, I like the Crysis Editor's the best. The next step will be to make the widget change appearance when the mouse is hovered over it. As has been requested in the past, the widget stays on top of everything drawn, and always scales to appear the same size at any distance.

  15. Josh
    One of our design principles for Leadwerks3D is to make development fast. You need to be able to iterate and re-iterate quickly, to try out ideas and make adjustments in as few of mouse clicks as possible. A real-time properties editor is definitely one way to enable that. In the screenshot below, I can adjust the intensity and color of the light dynamically, and see the results instantly. It feels so responsive and natural.
     
    I feel like this design philosophy gets us back to our roots with Cartography Shop and 3D World Studio. Those were tools built for level designers, by a level designer (me) who wanted better tools.
     

  16. Josh
    I don't have a clear and obvious subject for this blog, but I haven't posted in a while, so I wanted to write something. I've been getting in a lot of hours of development. My schedule on most days is wake up around 7, answer emails or test code on my Mac at home, then head over to the office at 10. Chris usually comes in right around then (we aren't too strict on schedules) and usually hangs around until around 6. I leave anywhere from 6-9 (and totally miss the rush hour traffic), go home, make some dinner, do some light coding until about 10, then play Left 4 Dead 2 for about 30 minutes and go to sleep.
     
    I'm having a blast with the editor features. The similarity to 3D World Studio puts us in better touch with our roots and core values, as a company, because 3D World Studio is where it all started. It was an editor borne out of a problem I had as a level designer. I'm picking and choosing the aspects I liked from that program, as well as the overall feel and philosophy behind it.
     
    For example, I decided the Leadwerks3D scene file format needs to be binary, not ASCII, because load times are important, both for high-end machines loading complex scenes, and low-end mobile devices loading simpler scenes. It's funny how optimization works both ways like that. The Leadwerks3D scene file format is very similar to the 3D World Studio map file format, with an object and string table. There's no complicated chunk structure designed to account for data types the loader doesn't recognize (which I have never ended up using). There's just a defined file structure, and if anything ever needs to be changed, a simple version number stored in the file will do it.
     
    On the other hand, we're calling scenes in Leadwerks3D "scenes" and not "maps". I feel like "maps" is an old-timey term applicable to first and second-generation FPS engines. These systems, while groundbreaking and awesome, didn't put the emphasis on tools that Leadwerks3D does, so I think it's important to get away from the old terminology associated with those programs.
     
    If you haven't seen it, there's some really cool work being done with Leadwerks Engine by a few community members. In particular, check out Klepto2's atmospheric scattering and Shadmar's ocean water.
     
    The possibility of a Leadwerks Engine graphics module for the Leadwerks3D beta has been suggested, and while I can't promise anything before trying it out to make sure, it is an intriguing possibility.
     
    Chris has some really cool particle effects on the way. Working as a team is a new experience for me, and the benefits became apparent to me last Friday. Chris was having trouble with code that makes billboarded particles rotate in the direction of the velocity, relative to the camera. He asked me for help and I tried a few times, then took a guess with a couple of transformation calls that turned out to be right. As a result, we now have an awesome particle system running, and it only took about 15 minutes of my attention to solve on particularly difficult problem. If I had to do that whole thing myself, it just wouldn't have gotten done, but we were able to leverage my skills to get that last piece in place, while Chris is doing a fantastic job with the overall system.
     
    However, we will not be going with the slogan I saw on his board the other day.

  17. Josh
    Here's an example of how setting a debug hook in Leadwerks3D from a BlitzMax program saved me a lot of time stepping through code to figure out where something went wrong. When the engine encounters a fatal error, the hook is called, giving control back to the main program.

     
    I can even step through the BlitzMax debugger and see which call to Leadwerks3D produced the error:

     
    And it works with all supported languages. Little things like this make programming with Leadwerks3D very enjoyable.
  18. Josh
    The properties editor in Leadwerks3D is a live dialog. Your changes are shown instantly in the editor. If multiple objects are selected and their properties do not have the same values, the property interface will show an "indeterminate" state until the value is changed:

     
    When an indeterminate value is changed, the new value is applied to all selected objects. Since they now have a matching value, it is now shown in the dialog. You can use this to move objects along a single axis, without making their other axis positions match:

     
    The program console now displays loading and deleting of assets in blue and purple, so you can easily see every file the engine loads. The interface is extremely customizable; every element can be moved around or popped off into its own window:

     
    Did I mention extreme UI flexibility?:

  19. Josh
    After putting together a pretty complete model of how the logic editor would work, I found myself unsatisfied with the properties dialog. I originally intended to make a standard properties dialog that opened in a separate window, with "OK", "Cancel", and "Apply" buttons. However, the logic editor and properties dialog were both being opened in separate windows that were a child of the main window, with no strict draw order between the two of them. I found it confusing to select an entity in the logic editor, and then have to go to a separate window to modify its properties.
     
    I tried moving the properties dialog into the scene browser panel, in the side panel on the right. There was a nice big empty space for it, and it looks appropriate.

     
    Normally I do not like interfaces like this because it means the data has to be continuously synced even when you are just editing in the viewports. However, with some clever coding that keeps performance in mind, I think I can get around this problem. It certainly feels a lot simpler to work with. With this many features in the editor, I want to be careful to retain the intuitiveness of programs like 3D World Studio,
     
    Property groups are separated by tabs, which I find to be a lot easier to use than a big vertical stack of properties as high as the screen. It also makes editing faster because I only have to fill in the values of the currently visible properties tab.
     
    I think it also makes sense to open assets in their own window, while modifying object properties in the property dialog embedded in the main window. You will spend more time modifying object properties while editing. Assets are something that only need to be adjusted once in a while, because they should be left alone and reused many times.
     
    The whole properties interface is a class that lets you build an interface, without actually knowing what each control does. The editor entity class has functions to get and set property strings, which can include position, rotation, and other basic stuff. It's quite nice because it allows me to add lots of new properties without having to hard-code any dialogs.
  20. Josh
    We've had a lot of discussion with the community about the Leadwerks3D script system. The current design is based on a combination of user feedback, lessons learned from Leadwerks Engine 2, stealing ideas from other game engines, and my own judgement. Our goal is to make a really easy and powerful game scripting system, without overwhelming the user with complexity. With that said, I thought I would let you in on how the script system presently is working in our own builds, and the process of using it.
     
    To attach a script to an entity, open the Properties Editor. Click and drag the script file from the Asset Browser onto the Properties Editor. A tab appears where all the script properties are displayed:

     
    Here's an alternate approach we are playing with. Here, the properties editor is embedded in the scene tab in the side panel. Since assets can't be dragged from the Asset Browser to the Properties Editor (since they are in different tabs) we just right-click on the entity in the Scene Browser, and a standard file request dialog appears, allowing you to choose a script file:

     
    Which do you like better?
     
    The visual logic editor in Leadwerks3D is called "Synapse". To add an entity into Synapse, drag the entity from the Scene Browser into the Synapse window. The entity will appear as a block, with its attached script components displayed below it in order. The color of the logical block corresponds to the object color in solid and wireframe viewports:

     
    You can see the script defines the output and input functions that are displayed in Synapse. Next I'll show you how entities in Synapse can be strung together to make sequences of events and gameplay logic.
  21. Josh
    As I always do when making design decisions, I tried out several approaches to the properties dialog. Previously, I wrote about embedding it in the side panel, below the scene browser. I liked being able to quickly modify object properties, but I couldn't drag assets like textures and materials from the Asset Browser to the Properties Editor, because they were under different tabs in the side panel. Because of that, I popped the Properties Editor back into its own tool window.

     
    The experience of using the Properties Editor in the side panel gave me the idea to have an "Instant Update" mode, in addition to the default "OK", "Cancel", and "Apply" buttons. When "Instant update" is checked, any changes to the Properties Editor are immediately sent to the selected objects, giving you more immediate visual feedback.
     
    To add a script component to an entity, drag a script file from the Asset Browser to anywhere on the Properties Editor and release. A new tab with the properties for the script component will appear. Parsing scripts and getting their exposed variables is the next step I will work on.
  22. Josh
    One thing I love about constructive solid geometry modeling is that texture mapping is sooooo much simpler than 3ds Max. Most of the time the automatic texture mapping works fine, and when you do need to adjust texture mapping by hand, CSG texture mapping tools are still much easier. The justify buttons line a texture up along a face, or a group of faces.
     
    Although using these tools is fun and easy, programming them is another matter. I dreaded the implementation of the texture justify buttons, but it wasn't that hard. It doesn't account for rotation and scale yet, but a basic implementation turned out to be surprisingly easy. I guess after writing 3D World Studio five times, I am starting to get the hang of this:

     
    Smooth groups are working as well. Leadwerks3D will be the first CSG editor ever (AFAIK) to support real-time smooth groups. In the example below, we have a beveled corner we want to make rounded:

     
    To round the corner, select all the faces you want smoothed together, and press one of the smoothing group buttons. This will assign the selected faces to use the smoothing group you press. You can add multiple smooth groups to a face. Faces that have one or more smooth groups in common will be smoothed together:

     
    Here is the resulting rounded corner:

     
    Finally, I am starting to narrow down the visual theme for the new editor. Since we use the standard Windows and Mac interfaces on each operating system, it makes sense to use the standard Windows Office/VS icons whenever possible. One side benefit is they also look great on Mac, strangely enough:

     

     

     

     

    That's all for now. I've had a lot of fun designing the "perfect" workflow for game development, and can't wait to show you all the finished product this summer!
  23. Josh
    The new CSG editor is coming along nicely. Yesterday I implemented CSG rotation and skewing, and today I got object center and edge selection working. 3D World Studio uses GL_SELECT mode to handle picking in orthographic viewports, but this stopped being supported in ATI drivers a few years ago! I implemented a CPU math routine for picking brush edges. In orthographics viewports, brushes can be selected by clicking their center or any edge, while models only use the center for selection. (This is how Valve's Hammer editor does it.) If this feels weird I can implement an edge-picking algorithm for models, but I think the speed of this would not be good with complex models/scenes.
     
    The editor is starting to look and feel like a real editor, and we'll soon be able to start producing the level for our example game. It's neat to see a tool with a modern take on a proven level design technique. I have some map files saving and loading. I'd like to use an ASCII based format if possible, but that may be too slow, especially on mobile. Here's a sample of the file format I am using right now:

    Map { Version: 3.0 Brush { ID: 203099704 Name: "Box" Position: -0.999999881, 10.0000000, -20.0000000 Rotation: 0.000000000, 0.000000000, 0.000000000 Scale: 1.00000000, 1.00000000, 1.00000000 Color: 1.00000000, 1.00000000, 1.00000000, 1.00000000 Material: "models\buildings\guardtower\plaster.mat" Vertices { 0: -1.00000000, -10.0000000, -10.0000000 1: 0.999999881, -10.0000000, -10.0000000 2: 0.999999881, -10.0000000, 10.0000000 3: -1.00000000, -10.0000000, 10.0000000 4: -1.00000000, 10.0000000, -10.0000000 5: 0.999999881, 10.0000000, -10.0000000 6: 0.999999881, 10.0000000, 10.0000000 7: -1.00000000, 10.0000000, 10.0000000 } Face { Indices: 0,1,2,3 Plane: 0.000000000, -1.00000000, 0.000000000, -10.0000000 MappingPlane[0]: -1.00000000, -0.000000000, -0.000000000, 0.000000000 MappingPlane[1]: -0.000000000, -1.79489679e-009, -1.00000000, 0.000000000 Material: "models\buildings\guardtower\plaster.mat" } Face { Indices: 7,6,5,4 Plane: 0.000000000, 1.00000000, 0.000000000, -10.0000000 MappingPlane[0]: -1.00000000, -0.000000000, -0.000000000, 0.000000000 MappingPlane[1]: -0.000000000, -1.79489679e-009, 1.00000000, 0.000000000 Material: "models\buildings\guardtower\plaster.mat" } Face { Indices: 0,3,7,4 Plane: -1.00000000, 0.000000000, 0.000000000, -1.00000000 MappingPlane[0]: -1.79489679e-009, -0.000000000, -1.00000000, 0.000000000 MappingPlane[1]: -0.000000000, -1.00000000, -0.000000000, 0.000000000 Material: "models\buildings\guardtower\plaster.mat" } Face { Indices: 5,6,2,1 Plane: 1.00000000, 0.000000000, 0.000000000, -0.999999881 MappingPlane[0]: -1.79489679e-009, -0.000000000, 1.00000000, 0.000000000 MappingPlane[1]: -0.000000000, -1.00000000, -0.000000000, 0.000000000 Material: "models\buildings\guardtower\plaster.mat" } Face { Indices: 4,5,1,0 Plane: -0.000000000, -0.000000000, -1.00000000, -10.0000000 MappingPlane[0]: 1.00000000, -0.000000000, 3.58979357e-009, 0.000000000 MappingPlane[1]: -0.000000000, -1.00000000, -0.000000000, 0.000000000 Material: "models\buildings\guardtower\plaster.mat" } Face { Indices: 6,7,3,2 Plane: -0.000000000, 0.000000000, 1.00000000, -10.0000000 MappingPlane[0]: -1.00000000, -0.000000000, -0.000000000, 0.000000000 MappingPlane[1]: -0.000000000, -1.00000000, -0.000000000, 0.000000000 Material: "models\buildings\guardtower\plaster.mat" } } }
  24. Josh
    CSG is a great way to quickly and easily produce levels, and the editor is even fun to write. I added another tab next to the scene and project tabs where you can select objects to create. The CSG texture mapping and smoothing controls also reside on this panel. I'm still playing around with the layout, but here is what I have now:

     
    CSG brushes are now entities, and can be arranged in a hierarchy. Check out the scene hierarchy that gets created when you create a compound brush object (a shape made of many brushes):

     
    You can drag brushes around and change the hierarchy, and traditional CSG editing still works, amazingly. I'm sure glad we have reliable transformation commands or this would be impossible!
     
    Here's another nice shot:

×
×
  • Create New...