Jump to content

Josh

Staff
  • Content Count

    15,824
  • Joined

  • Last visited

Everything posted by Josh

  1. I plan on paid updates every 12-18 months, but you can buy one version once and use forever.
  2. Josh

    Supernatural City

    man I could get you some interesting reference photos of some run down places.
  3. @Angelwolf Like this?: https://www.leadwerks.com/learn?page=API-Reference_Object_Entity_Camera_SetFogMode
  4. Working on GDC 2020 lecture proposal.

    1. Thirsty Panther
    2. Josh

      Josh

      VR in aerospace and performance optimization for rendering large CAD models in real-time.

    3. Thirsty Panther

      Thirsty Panther

      Interesting and catchy :)

  5. I guess I have only done "mute". He looks like he is riding regular. The flying squirrel is pretty awesome.
  6. So in other words make it temporal rather than distance-based? That makes a lot of sense. I never really thought about that before.
  7. Josh

    Shoot Simulator

    Very cool. I bet that can save a lot of money in basic training. No ammo, no weapon maintenance, no huge shooting range to go to.
  8. Sorry, I just meant that in a matter-of-fact way. The way the plugin system is set up, the data is always run through the GMF2 data format. So we can have a plugin that loads a GLTF or USD or other files and gives it to the engine in memory as a GMF2 file, or we can make it save the GMF2 for faster loading. Bottom line is if we have an import plugin for any format, we also automatically have a converter. Plus the file format is so simple I think we will see more community-made exporters than we did with GMF1, which had a "tricky" chunk structure. Plus, we have the GMF2 SDK, which makes it easy to just create some data structures and then the code saves a file for you. So GMF2 and its SDK is a big funnel we can easily pipe all kinds of data into and either save it or load it from memory. I think loading speed is a significant consideration we should be paying attention to. This seems like a really basic consideration we should be designing file formats around, for all games. I guess I need to get some definite numbers on what mesh data load times are like under realistic use cases.
  9. @Monkey Frog Studio You've convinced me to make GMF2 our default file format. If another standard is coming along that replaced GLTF, yet still does not give us fast-loading models, we need to invent our own. We will still provide direct loading and optional conversion of GLTF files, and maybe also this new format, but some of these huge CAD files we are working with would take ages to load if we have to sort through all the data. I think we will be able to show some loading speed comparisons between GLTF and GMF2, and the difference will be massive. I am going to introduce the file format at GDC 2020, along with tools for working with it.
  10. Notice there is actually no one talking about a game-ready model format, because I guess that is boring or something. OpenGEX, Collada, and FBX are "exchange formats" for moving data between modeling programs, and I guess this USD format is too. GLTF is a scene (not model) format for web graphics, which is why it uses JPG and PNG textures. No one even considers making something for normal 3D games. What do you think the advantage of this USD format is? GLTF is nice because at least you can load all the Sketchfab content up easily.
  11. Josh

    How to use gmax

    You might actually be able to use it for commercial games: https://www.turbosquid.com/Download/?ID=L567261&DLC=4GYYVMKP7G
  12. Josh

    How to use gmax

    Not a lot of people know about this, but back in 2001 Discreet (before the company was purchased by Autodesk) released a free version of 3ds max for modding games. Back then game file formats and tools were much more highly specialized than today, so each game required a "game pack" to customize the gmax interface to support that game. I think the idea was to charge the game developer money to add support for their game. Gmax supported several titles including Quake 3 Arena and Microsoft Flight Simulator, but was later discontinued. I personally love the program because it includes only the features you need from 3ds max for hard surface modeling. It's basically a stripped down version of 3ds max with only the features you need. Gmax still survives today, apparently in the custody of Turbosquid. You can download it here. You need the gmax 1.2 installer and the Tempest game pack for Quake 3. You will also need to request a free registration key from Turbosquid. After installing gmax, you can simply find the MD3 export plugin ("md3exp.dle") in the Tempest game pack download and copy that to your "C:\gmax\plugins" directory to enable export. There is also an optional There is also an optional MD3 import script by Chris Cookson which is uploaded here for safekeeping: q3-md3.ms With the plugin system in our new engine I was able to add support for loading Quake 3 MD3 models, so you can export your gmax models and load them up in the new engine. However, there are some restrictions. The MD3 file format uses compressed vertex positions, so your vertex positions have a limited range and resolution. Additionally, there are restrictions on what you can do with the gmax program, so take a look at the licensing terms before you do anything. Still, it's a fun program to have and this is a nice feature to play around with.
  13. Quake 3 Arena player models are split into three parts. If you want to load them up, this is how: //Load Quake 3 plugin auto md3loader = LoadPlugin("Plugins/MD3.dll"); //Load Q3A character auto head = LoadModel(world, "Models/MD3/bitterman/head.md3"); auto torso = LoadModel(world, "Models/MD3/bitterman/upper.md3"); auto lower = LoadModel(world, "Models/MD3/bitterman/lower.md3"); auto tag_head = torso->FindChild("tag_head"); head->SetParent(tag_head, false); auto tag_torso = lower->FindChild("tag_torso"); torso->SetParent(tag_torso, false);
  14. I looked at the source, and World::Clear() actually deletes all ref counts, so this is incorrect. Smart pointers are great because they fix all this confusion, yet there's none of the slowdown that full GC requires.
  15. Quake is what got me into computer programming in the first place, and now I work with NASA. I am very fond of that whole scene.
  16. GLTF is already available, right now. It will continue to be supported.
  17. I wanted to get a working example of the plugin system together. One thing led to another, and...well, this is going to be a very different blog. GMF2 The first thing I had to do is figure out a way for plugins to communicate with the main program. I considered using a structure they both share in memory, but those always inevitably get out of sync when either the structure changes or there is a small difference between the compilers used for the program and DLL. This scared me so I went with a standard file format to feed the data from the plugin to the main program. GLTF is a pretty good format but has some problems that makes me look for something else to use in our model loader plugins. It's text-based and loads slower. It's extremely complicated. There are 3-4 different versions of the file format and many many options to split it across multiple files, binary, text, or binary-to-text encoding. It's meant for web content, not PC games. Tons of missing functionality is added through a weird plugin system. For example, DDS is supported through a plugin, but a backup PNG has to be included in case the loaded doesn't support the extension. The GMF file format was used in Leadwerks. It's a custom fast-loading chunk-based binary file format. GMF2 is a simpler flat binary format updated with some extra features: Vertices are stored in a single array ready to load straight into GPU memory. Vertex displacement is supported. Compressed bitangents. Quad and line meshes are supported. LOD Materials and textures can be packed into the format or loaded from external files. PBR and Blinn-Phong materials Mesh bounding boxes are supported so they don't have to be calculated at load time. This means the vertex array never has to be iterated through, making large meshes load faster. I am not sure yet if GMF2 will be used for actual files or if it is just going to be an internal data format for plugins to use. GLTF will continue to be supported, but the format is too much of a mess to use for plugin data. Here's a cool five-minute logo: The format looks something like this: char[4] ID "GMF2" int version 200 int root //ID of the root entity int texture_count //number of textures int textures_pos //file position for texture array int materials_count //number of materials int materials_pos //file position for materials int nodes_count //number of nodes int nodes_pos //file position for nodes As you can see, it is really easy to read and really easy to write. There's enough complexity in this already. I'm not bored. I don't need to introduce unnecessary complexity into the design just so I can show off. There are real problems that need to be solved and making a "tricky" file format is not one of them. In Leadwerks 2, we had a bit of code called the "GMF SDK". This was written in BlitzMax, and allowed construction of a GMF file with easy commands. I've created new C++ code to create GMF2 files: //Create the file GMFFile* file = new GMFFile; //Create a model node = new GMFNode(file, GMF_TYPE_MODEL); //Add an LOD level GMFLOD* lod = node->AddLOD(); //Add a mesh GMFMesh* mesh = lod->AddMesh(3); //triangle mesh //Add a vertex mesh->AddVertex(0,0,0, 0,0,1, 0,0, 0,0, 255,255,255,255); mesh->AddVertex(0,0,1, 0,0,1, 0,0, 0,0, 255,255,255,255); mesh->AddVertex(0,1,1, 0,0,1, 0,0, 0,0, 255,255,255,255); //Add a triangle mesh->AddIndice(0); mesh->AddIndice(1); mesh->AddIndice(2); Once your GMFFile is constructed you can save it into memory with one command. The Turbo Plugin SDK is a little more low-level than the engine, so it includes a MemWriter class to help with this, since the engine Stream class is not present. As a test I am writing a Quake 3 MD3 import plugin and will provide the project and source as an example of how to use the Turbo Plugin SDK. Packages The ZIP virtual file system from Leadwerks is being expanded and formalized. You can load a Package object to add new virtual files to your project. These will also be loadable from the editor, so you can add new packages to a project, and the files will appear in the asset browser and file dialogs. (Package files are read-only.) Plugins will allow packages to be loaded from file formats besides ZIP, like Quake WADs or Half-Life GCF files. Notice we keep all our loaded items in variables or arrays because we don't want them to get auto-deleted. //Load Quake 3 plugins auto pk3reader = LoadPlugin("Plugins/PK3.dll"); auto md3loader = LoadPlugin("Plugins/MD3.dll"); auto bsploader = LoadPlugin("Plugins/Q3BSP.dll"); Next we load the game package files straight from the Quake game directory. This is just like the package system from Leadwerks. //Load Quake 3 game packages std::wstring q3apath = L"C:/Program Files (x86)/Steam/steamapps/common/Quake 3 Arena/baseq3"; auto dir = LoadDir(q3apath); std::vector<shared_ptr<Package> > q3apackages; for (auto file : dir) { if (Lower(ExtractExt(file)) == L"pk3") { auto pak = LoadPackage(q3apath + L"/" + file); if (pak) q3apackages.push_back(pak); } } Now we can start loading content directly from the game. //Load up some game content from Quake! auto head = LoadModel(world, "models/players/bitterman/head.md3"); auto upper = LoadModel(world, "models/players/bitterman/upper.md3"); auto lower = LoadModel(world, "models/players/bitterman/lower.md3"); auto scene = LoadScene(world, "maps/q3ctf2.bsp"); Modding I have a soft spot for modding because that is what originally got me into computer programming and game development. I was part of the team that made "Checkered Flag: Gold Cup" which was a spin-off on the wonderful Quake Rally mod: I expect in the new editor you will be able to browse through game files just as if they were uncompressed in your project file, so the new editor can act as a modding tool, for those who are so inclined. It's going to be interesting to see what people do with this. We can configure the new editor to run a launch script that handles map compiling and then launches the game. All the pieces are there to make the new editor a tool for modding games, like Discreet's old Gmax experiment. I am going to provide official support for Quake 3 because all the file formats are pretty easy to load, and because gmax can export to MD3 and it would be fun to load Gmax models. Other games can be supported by adding plugins. So here are some of the things the new plugin system will allow: Load content directly from other games and use it in your own game. I don't recommend using copyrighted game assets for commercial projects, but you could make a "mod" that replaces the engine and requires the player to own the original game. You could probably safely use content from any Valve games and release a commercial game on Steam. Use the editor as a tool to make Quake or other maps. Add plugin support for new file formats. This might all be a silly waste of time, but it's a way to get the plugin system working, and if we can make something flexible enough to build Quake maps, well that is a good test of the robustness of the system.
  18. local window = Window:Create() local context = Context:Create(window) local gui = GUI:Create(context) local base = gui:GetBase() base:SetScript("Scripts/GUI/Panel.lua") x=20 y=20 local sep=30 widget = Widget:Slider(x,y,300,20,base) y=y+sep widget = Widget:Slider(x,y,300,20,base) widget:SetStyle(SLIDER_TRACKBAR) widget:SetRange(1,20) y=y+sep while true do if window:Closed() then return end if window:KeyHit(Key.Escape) then return end while EventQueue:Peek() do local event = EventQueue:Wait() if event.id == Event.WidgetAction then System:Print("WidgetAction") elseif event.id == Event.WidgetSelect then System:Print("WidgetSelect") end end context:Sync() end
  19. Josh

    Tessellation in Action

    Smooth transitions between detail levels. Make one low-poly model and you're done. Better detail up-close. No need to adjust distance settings or anything, it just works.
  20. Very very good work was done today. Big things are coming.

  21. If you buy Leadwerks it is yours to use, forever. A new game engine called "Turbo" is being developed. You can pay a $5 subscription for access to the beta right now. When it is released, it will be available as either a subscription or a one-time purchase with annual optional paid updates.
  22. Josh

    Shader Families

    I started to implement quads for tessellation, and at that point the shader system reached the point of being unmanageable. Rendering an object to a shadow map and to a color buffer are two different processes that require two different shaders. Turbo introduces an early Z-pass which can use another shader, and if variance shadow maps are not in use this can be a different shader from the shadow shader. Rendering with tessellation requires another set of shaders, with one different set for each primitive type (isolines, triangles, and quads). And then each one of these needs a masked and opaque option, if alpha discard is enabled. All in all, there are currently 48 different shaders a material could use based on what is currently being drawn. This is unmanageable. To handle this I am introducing the concept of a "shader family". This is a JSON file that lists all possible permutations of a shader. Instead of setting lots of different shaders in a material, you just set the shader family one: shaderFamily: "PBR.json" Or in code: material->SetShaderFamily(LoadShaderFamily("PBR.json")); The shader family file is a big JSON structure that contains all the different shader modules for each different rendering configuration: Here are the partial contents of my PBR.json file: { "turboShaderFamily" : { "OPAQUE": { "default": { "base": { "vertex": "Shaders/PBR.vert.spv", "fragment": "Shaders/PBR.frag.spv" }, "depthPass": { "vertex": "Shaders/Depthpass.vert.spv" }, "shadow": { "vertex": "Shaders/Shadow.vert.spv" } }, "isolines": { "base": { "vertex": "Shaders/PBR_Tess.vert.spv", "tessellationControl": "Shaders/Isolines.tesc.spv", "tessellationEvaluation": "Shaders/Isolines.tese.spv", "fragment": "Shaders/PBR_Tess.frag.spv" }, "shadow": { "vertex": "Shaders/DepthPass_Tess.vert.spv", "tessellationControl": "Shaders/DepthPass_Isolines.tesc.spv", "tessellationEvaluation": "Shaders/DepthPass_Isolines.tese.spv" }, "depthPass": { "vertex": "Shaders/DepthPass_Tess.vert.spv", "tessellationControl": "DepthPass_Isolines.tesc.spv", "tessellationEvaluation": "DepthPass_Isolines.tese.spv" } }, "triangles": { "base": { "vertex": "Shaders/PBR_Tess.vert.spv", "tessellationControl": "Shaders/Triangles.tesc.spv", "tessellationEvaluation": "Shaders/Triangles.tese.spv", "fragment": "Shaders/PBR_Tess.frag.spv" }, "shadow": { "vertex": "Shaders/DepthPass_Tess.vert.spv", "tessellationControl": "Shaders/DepthPass_Triangles.tesc.spv", "tessellationEvaluation": "Shaders/DepthPass_Triangles.tese.spv" }, "depthPass": { "vertex": "Shaders/DepthPass_Tess.vert.spv", "tessellationControl": "DepthPass_Triangles.tesc.spv", "tessellationEvaluation": "DepthPass_Triangles.tese.spv" } }, "quads": { "base": { "vertex": "Shaders/PBR_Tess.vert.spv", "tessellationControl": "Shaders/Quads.tesc.spv", "tessellationEvaluation": "Shaders/Quads.tese.spv", "fragment": "Shaders/PBR_Tess.frag.spv" }, "shadow": { "vertex": "Shaders/DepthPass_Tess.vert.spv", "tessellationControl": "Shaders/DepthPass_Quads.tesc.spv", "tessellationEvaluation": "Shaders/DepthPass_Quads.tese.spv" }, "depthPass": { "vertex": "Shaders/DepthPass_Tess.vert.spv", "tessellationControl": "DepthPass_Quads.tesc.spv", "tessellationEvaluation": "DepthPass_Quads.tese.spv" } } } } } A shader family file can indicate a root to inherit values from. The Blinn-Phong shader family pulls settings from the PBR file and just switches some of the fragment shader values. { "turboShaderFamily" : { "root": "PBR.json", "OPAQUE": { "default": { "base": { "fragment": "Shaders/Blinn-Phong.frag.spv" } }, "isolines": { "base": { "fragment": "Shaders/Blinn-Phong_Tess.frag.spv" } }, "triangles": { "base": { "fragment": "Shaders/Blinn-Phong_Tess.frag.spv" } }, "quads": { "base": { "fragment": "Shaders/Blinn-Phong_Tess.frag.spv" } } } } } If you want to implement a custom shader, this is more work because you have to define all your changes for each possible shader variation. But once that is done, you have a new shader that will work with all of these different settings, which in the end is easier. I considered making a more complex inheritance / cascading schema but I think eliminating ambiguity is the most important goal in this and that should override any concern about the verbosity of these files. After all, I only plan on providing a couple of these files and you aren't going to need any more unless you are doing a lot of custom shaders. And if you are, this is the best solution for you anyways. Consequently, the baseShader, depthShader, etc. values in the material file definition are going away. Leadwerks .mat files will always use the Blinn-Phong shader family, and there is no way to change this without creating a material file in the new JSON material format. The shader class is no longer derived from the Asset class because it doesn't correspond to a single file. Instead, it is just a dumb container. A ShaderModule class derived from the Asset class has been added, and this does correspond with a single .spv file. But you, the user, won't really need to deal with any of this. The result of this is that one material will work with tessellation enabled or disabled, quad, triangle, or line meshes, and animated meshes. I also added an optional parameter in the CreatePlane(), CreateBox(), and CreateQuadSphere() commands that will create these primitives out of quads instead of triangles. The main reason for supporting quad meshes is that the tessellation is cleaner when quads are used. (Note that Vulkan still displays quads in wireframe mode as if they are triangles. I think the renderer probably converts them to normal triangles after the tessellation stage.) I also was able to implement PN Quads, which is a quad version of the Bezier curve that PN Triangles add to tessellation. Basically all the complexity is being packed into the shader family file so that these decisions only have to be made once instead of thousands of times for each different material.
  23. With tessellation now fully implemented, I was very curious to see how it would perform when applied to arbitrary models. With tessellation, vertices act like control points for a Bezier mesh that is subdivided dynamically in screen space. Could tessellation be used to add new details to any low-poly model? Here is a low-res character model with a pointy head and obvious sharp edges all around his silhouette: When tessellation is enabled, the sharp edges go away and the mesh magically appears more detailed: Let's take a look at the "Doom Seeker" enemy model from Doom 3. The original model is 640 triangles: Tessellation gets rid of the blocky outline. And displacement adds new details to the model. What about other shapes? Can this technique be used to turn any low-poly model into a high-res version? Here is a tire model from "The Zone" DLC. Even with displacement set to zero on edge vertices, cracks still appear. So my conclusion on this is that tessellation is fantastic for closed organic shapes. In fact, I think it totally replaces separate LOD meshes for rocks, characters, and other organic shapes. However, it is not something you should use everywhere without any discretion.
  24. Previously I talked about the technical details of hardware tessellation and what it took to make it truly useful. In this article I will talk about some of the implications of this feature and the more advanced ramifications of baking tessellation into Turbo Game Engine as a first-class feature in the Although hardware tessellation has been around for a few years, we don't see it used in games that often. There are two big problems that need to be overcome. We need a way to prevent cracks from appearing along edges. We need to display a consistent density of triangles on the screen. Too many polygons is a big problem. I think these issues are the reason you don't really see much use of tessellation in games, even today. However, I think my research this week has created new technology that will allow us to make use of tessellation as an every-day feature in our new Vulkan renderer. Per-Vertex Displacement Scale Because tessellation displaces vertices, any discrepancy in the distance or direction of the displacement, or any difference in the way neighboring polygons are subdivided, will result in cracks appearing in the mesh. To prevent unwanted cracks in mesh geometry I added a per-vertex displacement scale value. I packed this value into the w component of the vertex position, which was not being used. When the displacement strength is set to zero along the edges the cracks disappear: Segmented Primitives With the ability to control displacement on a per-vertex level, I set about implementing more advanced model primitives. The basic idea is to split up faces so that the edge vertices can have their displacement scale set to zero to eliminate cracks. I started with a segmented plane. This is a patch of triangles with a user-defined size and resolution. The outer-most vertices have a displacement value of 0 and the inner vertices have a displacement of 1. When tessellation is applied to the plane the effect fades out as it reaches the edges of the primitive: I then used this formula to create a more advanced box primitive. Along the seam where the edges of each face meet, the displacement smoothly fades out to prevent cracks from appearing. The same idea was applied to make segmented cylinders and cones, with displacement disabled along the seams. Finally, a new QuadSphere primitive was created using the box formula, and then normalizing each vertex position. This warps the vertices into a round shape, creating a sphere without the texture warping that spherical mapping creates. It's amazing how tessellation and displacement can make these simple shapes look amazing. Here is the full list of available commands: shared_ptr<Model> CreateBox(shared_ptr<World> world, const float width = 1.0); shared_ptr<Model> CreateBox(shared_ptr<World> world, const float width, const float height, const float depth, const int xsegs = 1, const int ysegs = 1); shared_ptr<Model> CreateSphere(shared_ptr<World> world, const float radius = 0.5, const int segments = 16); shared_ptr<Model> CreateCone(shared_ptr<World> world, const float radius = 0.5, const float height = 1.0, const int segments = 16, const int heightsegs = 1, const int capsegs = 1); shared_ptr<Model> CreateCylinder(shared_ptr<World> world, const float radius = 0.5, const float height=1.0, const int sides = 16, const int heightsegs = 1, const int capsegs = 1); shared_ptr<Model> CreatePlane(shared_ptr<World> world, cnst float width=1, const float height=1, const int xsegs = 1, const int ysegs = 1); shared_ptr<Model> CreateQuadSphere(shared_ptr<World> world, const float radius = 0.5, const int segments = 8); Edge Normals I experimented a bit with edges and got some interesting results. If you round the corner by setting the vertex normal to point diagonally, a rounded edge appears. If you extend the displacement scale beyond 1.0 you can get a harder extended edge. This is something I will experiment with more. I think CSG brush smooth groups could be used to make some really nice level geometry. Screen-space Tessellation LOD I created an LOD calculation formula that attempts to segment polygons into a target size in screen space. This provides a more uniform distribution of tessellated polygons, regardless of the original geometry. Below are two cylinders created with different segmentation settings, with tessellation disabled: And now here are the same meshes with tessellation applied. Although the less-segmented cylinder has more stretched triangles, they both are made up of triangles about the same size. Because the calculation works with screen-space coordinates, objects will automatically adjust resolution with distance. Here are two identical cylinders at different distances. You can see they have roughly the same distribution of polygons, which is what we want. The same amount of detail will be used to show off displaced edges at any distance. We can even set a threshold for the minimum vertex displacement in screen space and use that to eliminate tessellation inside an object and only display extra triangles along the edges. This allows you to simply set a target polygon size in screen space without adjusting any per-mesh properties. This method could have prevented the problems Crysis 2 had with polygon density. This also solves the problem that prevented me from using tessellation for terrain. The per-mesh tessellation settings I worked on a couple days ago will be removed since it is not needed. Parallax Mapping Fallback Finally, I added a simple parallax mapping fallback that gets used when tessellation is disabled. This makes an inexpensive option for low-end machines that still conveys displacement. Next I am going to try processing some models that were not designed for tessellation and see if I can use tessellation to add geometric detail to low-poly models without any cracks or artifacts.
  25. @Rick Maybe because models have to be created without UV seams or cracks in displacement will appear. I am adding a displacement vertex value so that displacement can be pulled in along UV seams. I see tessellation being a common thing in two areas: terrain and brushes. With brush geometry I can add some extra vertices so that displacement gets tucked in along the edges and you never have to worry about it. It's common for PBR materials to include a displacement map so I think tessellation will be much more common in Turbo. Perhaps a script tool can also be created that detects UV seams and sets those vertices' displacement to zero.
×
×
  • Create New...