Jump to content

GMF2, Plugins, and Modding

Josh

763 views

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:

gmf2.png.e4f8a73b44c3737353080d4dcb82b16c.png

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.

bitterman.jpg.93ad860beedacf9cd9033e5a3584bc94.jpg

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.

gmaxsplash.jpg.3afd806e4ce5cbd58dc1d0e03c696b98.jpg

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.

 

  • Like 4


12 Comments


Recommended Comments

25 minutes ago, aiaf said:

We would be able to load gltf models? 

GLTF is already available, right now. It will continue to be supported.

Share this comment


Link to comment

I shared this in the Quake modding discord server since you mentioned quake 3 mapping a few times. Hopefully it drives some more interest into turbo :)

  • Like 1
  • Haha 1

Share this comment


Link to comment
40 minutes ago, Mr_SquarePeg said:

I shared this in the Quake modding discord server since you mentioned quake 3 mapping a few times. Hopefully it drives some more interest into turbo :)

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.

Share this comment


Link to comment

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.

Share this comment


Link to comment

USD is used to transfer, well, anything you can do in a 3D modeling program. So, it doesn't matter if you use 3DS Max, Maya, or even Blender. If you can import/export out USD, you can bring the entire scene (models, materials, animations (including bones, etc.), lights ... everything, in one shot (if you want). Unity already supports USD. Unreal is getting ready to (if they don't already). And most 3D modeling programs do, too. Apparently it has advantages over GLTF. 

USD Primer

Share this comment


Link to comment

What is USD?

Pipelines capable of producing computer graphics films and games typically generate, store, and transmit great quantities of 3D data, which we call "scene description".  Each of many cooperating applications in the pipeline  (modeling, shading, animation, lighting, fx, rendering) typically has its own special form of scene description tailored to the specific needs and workflows of the application, and neither readable nor editable by any other application.   Universal Scene Description (USD) is the first publicly available software that addresses the need to robustly and scalably interchange and augment arbitrary 3D scenes that may be composed from many elemental assets.  

From: https://graphics.pixar.com/usd/docs/index.html

Share this comment


Link to comment

@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.

Share this comment


Link to comment

 

Quote

You've convinced me to make GMF2 our default file format.

Well, firstly, I didn't ask for it to be the DEFAULT file format, but recommended it as a format that could be supported by Leadwerks. And that was at least a year ago, too. Time moves on. Things progress. And as you worked with GLTF you found things you both liked and did not like. Due to the things you didn't like about GLTF (some of them mentioned in your post here), I then recommended USD as a possibility. Some are saying there are advantages USD has over GLTF. So, is there really any harm in looking at it? If you find that GLTF is great for what you want, then good. You've already done the work (or most of it), right? If you find USD is a viable option and provides benefits, then wouldn't you be happy to know about it?
 

Quote

If another standard is coming along that replaced GLTF, yet still does not give us fast-loading models, we need to invent our own.

And when you do that, you then need a way for artists to get their content into your engine. That means exporting from their 3D package of choice into your custom file format. And that means someone creating and maintaining these export plugins for the various packages, such as Maya, 3DS Max, C4D, MODO, and Blender. 

Quote

We will still provide direct loading and optional conversion of GLTF files, and maybe also this new format,

Glad to hear it.

Quote

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.

That's a standard issue with CAD files in a non-CAD application.

Share this comment


Link to comment

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.

Share this comment


Link to comment

 That all sounds pretty awesome, actually. Thanks for taking the time to fill me in. Selfishly, all I care about, as an artist, is getting my assets into the engine as easily as possible. The less hoops I have to jump through, the better. :)

  • Haha 1

Share this comment


Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Blog Entries

    • By Josh in Josh's Dev Blog 3
      I wanted to work on something a bit different, and this sure is different. I've got a framework of a new particle system worked out. What's really special about this system is the amount of interactivity the particles will allow.
      Particle-world collisions. Particle-particle collisions (repulsion) Particle-particle cohesion (fluids with surface tension) Instead of just being a visual effect, I want our new particles to be fully interactive with physics so that particles can exert forces on objects. This will allow you to simulate fluids, smoke, and other effects in a realistic manner, not just dumb collision of particles bounding off walls. It should even be possible to simulate hydrophobic and hydrophillic liquids if you mix two together with different cohesion values.
      Basically what I want is something like Nvidia Flow on the CPU and exerting forces on the world. So if you had water falling on a water wheel the wheel would move because of the forces, or a blast of wind could knock objects over without any special force fields or other fake effects.
      I also have a technique worked out that will allow lighting of clouds and other masses of gas, with back-scattering.
      Emitters can be instanced so if you have one really high-quality torch effect, for example, you can instance it and use it as much as you like without any additional computational cost per instance.
      Particle emitters can be controlled with a Lua script or C++ actor. Two new functions are available, UpdateParticle() and EmitParticle(). Here is a script that controls particle behavior over time:
      entity.particleVelocity = Vec3(0,0,1) entity.particleAcceleration = Vec3(0,-1,0) entity.inverseSquareFalloff = true entity.particleRadiusBegin = 0.1 entity.particleRadiusEnd = 0.2 entity.particleColorBegin = Vec4(1,1,1,1) entity.particleColorEnd = Vec4(1,1,1,0) entity.particleMass = 1 entity.particleSpin = 5 function entity:Start() self.particleColorBeginHSL = HSL(self.particleColorBegin.rgb) self.particleColorEndHSL = HSL(self.particleColorEnd.rgb) local emitter = Emitter(self) if emitter == nil then return end local n for n = 1, #emitter.particles do emitter.particles[n].mass = self.particleMass emitter.particles[n].falloff = (n-1) / (#emitter.particles - 1) end end function entity:EmitParticle(index) local emitter = Emitter(self) if emitter == nil then return end emitter.particles[index].position = self:GetPosition(true) emitter.particles[index].velocity = TransformVector(self.particleVelocity,self,nil) emitter.particles[index].radius = self.particleRadiusBegin emitter.particles[index].color = self.particleColorBegin end function entity:UpdateParticle(index) local emitter = Emitter(self) if emitter == nil then return end emitter.particles[index].velocity = emitter.particles[index].velocity + self.particleAcceleration / 60 local falloff = emitter.particles[index].falloff if self.inverseSquareFalloff then falloff = falloff * falloff end emitter.particles[index].color.rgb = RGB(self.particleColorBeginHSL * (1 - falloff) + self.particleColorEndHSL * falloff) emitter.particles[index].color.a = self.particleColorBegin.a * (1 - falloff) + self.particleColorEnd.a * falloff emitter.particles[index].radius = self.particleRadiusBegin * (1 - falloff) + self.particleRadiusEnd * falloff emitter.particles[index].rotation = emitter.particles[index].rotation + self.particleSpin / 60 end A different script could be used to make particles emit from vertices of a model, to make the model appear to be on fire, or other effects. This will allow infinite customization to create any behavior you want.
      Particle physics will be calculated on the physics thread so I expect them to be very fast.
    • By Josh in Josh's Dev Blog 6
      For finer control over what 2D elements appear on what camera, I have implemented a system of "Sprite Layers". Here's how it works:
      A sprite layer is created in a world. Sprites are created in a layer. Layers are attached to a camera (in the same world). The reason the sprite layer is linked to the world is because the render tweening operates on a per-world basis, and it works with the sprite system just like the entity system. In fact, the rendering thread uses the same RenderNode class for both.
      I have basic GUI functionality working now. A GUI can be created directly on a window and use the OS drawing commands, or it can be created on a sprite layer and rendered with 3D graphics. The first method is how I plan to make the new editor user interface, while the second is quite flexible. The most common usage will be to create a sprite layer, attach it to the main camera, and add a GUI to appear in-game. However, you can just as easily attach a sprite layer to a camera that has a texture render target, and make the GUI appear in-game on a panel in 3D. Because of these different usages, you must manually insert events like mouse movements into the GUI in order for it to process them:
      while true do local event = GetEvent() if event.id == EVENT_NONE then break end if event.id == EVENT_MOUSE_DOWN or event.id == EVENT_MOUSE_MOVE or event.id == EVENT_MOUSE_UP or event.id == EVENT_KEY_DOWN or event.id == EVENT_KEY_UP then gui:ProcessEvent(event) end end You could also input your own events from the mouse position to create interactive surfaces, like in games like DOOM and Soma. Or you can render the GUI to a texture and interact with it by feeding in input from VR controllers.

      Because the new 2D drawing system uses persistent objects instead of drawing commands the code to display elements has changed quite a lot. Here is my current button script. I implemented a system of abstract GUI "rectangles" the script can create and modify. If the GUI is attached to a sprite layer these get translated into sprites, and if it is attached directly to a window they get translated into system drawing commands. Note that the AddTextRect doesn't even allow you to access the widget text directly because the widget text is stored in a wstring, which supports Unicode characters but is not supported by Lua.
      --Default values widget.pushed=false widget.hovered=false widget.textindent=4 widget.checkboxsize=14 widget.checkboxindent=5 widget.radius=3 widget.textcolor = Vec4(1,1,1,1) widget.bordercolor = Vec4(0,0,0,0) widget.hoverbordercolor = Vec4(51/255,151/255,1) widget.backgroundcolor = Vec4(0.2,0.2,0.2,1) function widget:MouseEnter(x,y) self.hovered = true self:Redraw() end function widget:MouseLeave(x,y) self.hovered = false self:Redraw() end function widget:MouseDown(button,x,y) if button == MOUSE_LEFT then self.pushed=true self:Redraw() end end function widget:MouseUp(button,x,y) if button == MOUSE_LEFT then self.pushed = false if self.hovered then EmitEvent(EVENT_WIDGET_ACTION,self) end self:Redraw() end end function widget:OK() EmitEvent(EVENT_WIDGET_ACTION,self) end function widget:KeyDown(keycode) if keycode == KEY_ENTER then EmitEvent(EVENT_WIDGET_ACTION,self) self:Redraw() end end function widget:Start() --Background self:AddRect(self.position, self.size, self.backgroundcolor, false, self.radius) --Border if self.hovered == true then self:AddRect(self.position, self.size, self.hoverbordercolor, true, self.radius) else self:AddRect(self.position, self.size, self.bordercolor, true, self.radius) end --Text if self.pushed == true then self:AddTextRect(self.position + iVec2(1,1), self.size, self.textcolor, TEXT_CENTER + TEXT_MIDDLE) else self:AddTextRect(self.position, self.size, self.textcolor, TEXT_CENTER + TEXT_MIDDLE) end end function widget:Draw() --Update position and size self.primitives[1].position = self.position self.primitives[1].size = self.size self.primitives[2].position = self.position self.primitives[2].size = self.size self.primitives[3].size = self.size --Update the border color based on the current hover state if self.hovered == true then self.primitives[2].color = self.hoverbordercolor else self.primitives[2].color = self.bordercolor end --Offset the text when button is pressed if self.pushed == true then self.primitives[3].position = self.position + iVec2(1,1) else self.primitives[3].position = self.position end end This is arguably harder to use than the Leadwerks 4 system, but it gives you advanced capabilities and better performance that the previous design did not allow.
    • By reepblue in reepblue's Blog 1
      Premake is multiplication project maker.Unlike CMake, it simply generates a project file for the given IDE giving you a clean result. You only need the one light weight executable and a lua script for this to work.  I've spent today setting it up with Leadwerks. I haven't tested Linux yet, but it should work.
      My premake5.lua file:
      g_LeadwerksHeaderPath = "./Engine/Include" g_LeadwerksLibPath = "./Engine/Libs" function GlobalSettings() -- Include Directories includedirs { "%{prj.name}", "%{g_LeadwerksHeaderPath}", "%{g_LeadwerksHeaderPath}/Libraries/SDL2-2.0.10/include", "%{g_LeadwerksHeaderPath}/Libraries/NewtonDynamics/sdk/dgCore", "%{g_LeadwerksHeaderPath}/Libraries/NewtonDynamics/sdk/dgNewton", "%{g_LeadwerksHeaderPath}/Libraries/libvorbis/include", "%{g_LeadwerksHeaderPath}/Libraries/libogg/include", "%{g_LeadwerksHeaderPath}/Libraries/openssl/include", "%{g_LeadwerksHeaderPath}/Libraries/VHACD/src/VHACD_Lib/inc", "%{g_LeadwerksHeaderPath}/Libraries/glslang", "%{g_LeadwerksHeaderPath}/Libraries/freetype-2.4.7/include", "%{g_LeadwerksHeaderPath}/Libraries/OpenAL/include", "%{g_LeadwerksHeaderPath}/Libraries/NewtonDynamics/sdk/dMath", "%{g_LeadwerksHeaderPath}/Libraries/NewtonDynamics/sdk/dgTimeTracker", "%{g_LeadwerksHeaderPath}/Libraries/NewtonDynamics/sdk/dContainers", "%{g_LeadwerksHeaderPath}/Libraries/NewtonDynamics/sdk/dCustomJoints", "%{g_LeadwerksHeaderPath}/Libraries/RecastNavigation/RecastDemo/Include", "%{g_LeadwerksHeaderPath}/Libraries/RecastNavigation/DetourCrowd/Include", "%{g_LeadwerksHeaderPath}/Libraries/RecastNavigation/DetourTileCache/Include", "%{g_LeadwerksHeaderPath}/Libraries/RecastNavigation/DebugUtils/Include", "%{g_LeadwerksHeaderPath}/Libraries/RecastNavigation/Recast/Include", "%{g_LeadwerksHeaderPath}/Libraries/RecastNavigation/Detour/Include", "%{g_LeadwerksHeaderPath}/Libraries/tolua++-1.0.93/include", "%{g_LeadwerksHeaderPath}/Libraries/lua-5.1.4", "%{g_LeadwerksHeaderPath}/Libraries/glew-1.6.0/include/GL", "%{g_LeadwerksHeaderPath}/Libraries/glew-1.6.0/include", "%{g_LeadwerksHeaderPath}/Libraries/enet-1.3.1/include", "%{g_LeadwerksHeaderPath}/Libraries/zlib-1.2.5", "%{g_LeadwerksHeaderPath}/Libraries/freetype-2.4.3/include" } -- Global Defines: defines { "__STEAM__", "_CUSTOM_JOINTS_STATIC_LIB", "FT2_BUILD_LIBRARY", "LEADWERKS_3_1", "DG_DISABLE_ASSERT", "OPENGL", "_NEWTON_STATIC_LIB", "_STATICLIB" } -- Windows Exclusive: filter "system:windows" systemversion "latest" pchsource "%{prj.name}/stdafx.cpp" links { "libcryptoMT.lib", "libsslMT.lib", "Rpcrt4.lib", "crypt32.lib", "libcurl.lib", "msimg32.lib", "lua51.lib", "steam_api.lib", "ws2_32.lib", "Glu32.lib", "libovrd.lib", "OpenGL32.lib", "winmm.lib", "Psapi.lib", "OpenAL32.lib", "SDL2.lib", "Leadwerks.lib" } libdirs { "%{g_LeadwerksLibPath}/Windows/x86", "%{g_LeadwerksLibPath}/Windows/x86/%{cfg.buildcfg}" } defines { "PSAPI_VERSION=1", "PTW32_STATIC_LIB", "PTW32_BUILD", "_NEWTON_USE_LIB", "_LIB", "DG_USE_NORMAL_PRIORITY_THREAD", "GLEW_STATIC", "WINDOWS", "WIN32", "OS_WINDOWS", "PLATFORM_WINDOWS", "_WIN_32_VER" } buildoptions { "/D \"SLB_LIBRARY\"", } flags { "NoMinimalRebuild" } linkoptions { "/NODEFAULTLIB:MSVCRT.lib", "/NODEFAULTLIB:MSVCRTD.lib" } -- Linux Exclusive: filter "system:linux" systemversion "latest" linkoptions { "-ldl", "-lopenal", "-lGL", "-lGLU", "-lX11", "-lXext", "-lXrender", "-lXft", "-lpthread", "-lcurl", --"-lSDL2", "%{g_LeadwerksLibPath}/Linux/libluajit.a", "%{gameDir}/libopenvr_api.so" } defines { "ZLIB", "PLATFORM_LINUX", "unix", "_POSIX_VER", "_POSIX_VER_64", "DG_THREAD_EMULATION", "DG_USE_THREAD_EMULATION", "GL_GLEXT_PROTOTYPES", "LUA_USE_LINUX", "_GLIBCXX_USE_CXX11_ABI", "_CUSTOM_JOINTS_STATIC_LIB" } linkoptions { "%{g_LeadwerksLibPath}/Linux/%{cfg.buildcfg}/Leadwerks.a" } -- Debug Build: filter "configurations:Debug" runtime "Debug" symbols "on" targetsuffix ".debug" defines { "DEBUG", "_DEBUG" } if os.target() == "windows" then links { "newton_d.lib", "dContainers_d.lib", "dCustomJoints_d.lib" } end -- Release Build: filter "configurations:Release" runtime "Release" optimize "on" if os.target() == "windows" then buildoptions { "/MP" } links { "newton.lib", "dContainers.lib", "dCustomJoints.lib" } end end function GenerateLuaApp() workspace "PremakeTest" architecture "x86" --architecture "x86_64" startproject "LuaApp" configurations { "Debug", "Release" } -- Test application project "LuaApp" kind "ConsoleApp" language "C++" location "%{prj.name}" staticruntime "on" -- Project Directory: projectDir = "%{prj.name}/" -- Game Directory: gameDir = _WORKING_DIR .. "/../Projects/%{prj.name}" -- OBJ Directory objdir (projectDir .. "%{cfg.buildcfg}_%{prj.name}") targetdir (gameDir) files { "%{prj.name}/**.h", "%{prj.name}/**.cpp" } pchheader "stdafx.h" -- Custom Defines defines { "__TEST_ME_", } GlobalSettings() end newaction { trigger = "luaapp", description = "Builds the stock lua app", execute = GenerateLuaApp() } if _ACTION == "luaapp" then GenerateLuaApp() end Then I just have batch file that builds the project file.
      "devtools/premake5.exe" vs2017 luaapp pause My impressions are more positive on this than CMake as CMake creates a lot of extra files to do real time updating if the CMakeList file is updated. Premake simply just builds the project file and that's it. It really reminds me of VPC stuff I had to deal with in my modding days. Really interested to how codelite projects generate on Linux.
×
×
  • Create New...