Jump to content


  • Content Count

  • Joined

  • Last visited

Community Reputation

622 Excellent


About reepblue

  • Rank
    Advanced Member

Profile Information

  • Gender
    Not Telling

Recent Profile Visitors

18,019 profile views
  1. I think you can remove an app from Steam and only existing people who have it in their library still has access to it.
  2. This way a probably an undo bug. I would try reloading the map and if you can't reload the map due to an error, at least it's a very simple scene.
  3. Man, the screenshot on the right doesn't even look like Leadwerks, great work!
  4. I do think you need something like the auto lod system. Nobody likes LOD models anyway.
  5. That actually makes more sense if you're coming from something quake based. But yeah, the models have to be dividable by 2 inorder for it to snap correctly. 2.56m is the height of a wall.
  6. Make sure you apply your scale under the Object tab in blender. Refer to my blog post about custom shapes for more information.
  7. This was my output settings with Blender 2.79b
  8. For some reason, I've been seeing a lot of questions on how to add actors created into C++ recently. My first attempt on this was in 2016 with Crawler's Den; A modern remake of the SDK example level. That was on a version of Leadwerks in which there was no official Actor system in place. Today, the engine has an Actor class which can be attached to any entity, but besides some loose examples, it's not well documented. Also there is no official way on linking your entities in the editor with your actors. But today I wish to share with you some insight on how I managed to get not only actors attaching in my maps, but also allowing interaction with the flowgraph system. Disclaimer: This is more copy and paste code only tested with Leadwerks 4. Base Actor And Actor Factory First, you'll want to make a base class off the engine's Actor class that'll allow us to easily grab values from the lua file. Yes, we can have an Actor and a Script attached to the same entity at a time! The idea is that the lua script will be our definition file for our actor to load on map load. #ifndef BASEACTOR_H #define BASEACTOR_H #if defined( _WIN32 ) #pragma once #endif #include "stdafx.h" #define ACTOR_KEYVALUE "classname" class BaseActor : public Actor { public: void SetBoolValue(const std::string& pValue, const bool pDefault = false); bool GetBoolValue(const std::string& pValue, const bool pDefault = false); void SetFloatValue(const std::string& pValue, const float pDefault = 0); float GetFloatValue(const std::string& pValue, const float pDefault = 0); void SetIntValue(const std::string& pValue, const int pDefault = 0); int GetIntValue(const std::string& pValue, const int pDefault = 0); void SetStringValue(const std::string& pValue, const std::string& pDefault = ""); std::string GetStringValue(const std::string& pValue, const std::string& pDefault = ""); Vec2* GetVec2Value(const std::string& pValue, Vec2* pDefault = 0); Vec3* GetVec3Value(const std::string& pValue, Vec3* pDefault = 0); Vec4* GetVec4Value(const std::string& pValue, Vec4* pDefault = 0); Entity* GetEntityValue(const std::string& pValue, Entity* pDefault = NULL); void FireOutput(const std::string& pEvent); }; #endif #include "stdafx.h" #include "baseactor.h" //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void BaseActor::SetBoolValue(const std::string& pValue, const bool pDefault) { #ifndef LEADWERKS_5 GetEntity()->SetBool(pValue, pDefault); #else GetEntity()->SetBoolean(pValue, pDefault); #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool BaseActor::GetBoolValue(const std::string& pValue, const bool pDefault) { if (GetEntity() == nullptr) return pDefault; #ifndef LEADWERKS_5 if (entity->GetBool(pValue) != pDefault) { return entity->GetBool(pValue); } #else if (GetEntity()->GetBoolean(pValue) != pDefault) { return GetEntity()->GetBoolean(pValue); } #endif return pDefault; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void BaseActor::SetFloatValue(const std::string& pValue, const float pDefault) { #ifndef LEADWERKS_5 GetEntity()->SetFloat(pValue, pDefault); #else GetEntity()->SetNumber(pValue, (float)pDefault); #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- float BaseActor::GetFloatValue(const std::string& pValue, const float pDefault) { if (GetEntity() == nullptr) return pDefault; #ifndef LEADWERKS_5 if (entity->GetFloat(pValue) != NULL) { return entity->GetFloat(pValue); } #else if (GetEntity()->GetNumber(pValue) != NULL) { return (float)GetEntity()->GetNumber(pValue); } #endif return pDefault; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void BaseActor::SetIntValue(const std::string& pValue, const int pDefault) { GetEntity()->SetString(pValue, to_string(pDefault)); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int BaseActor::GetIntValue(const std::string& pValue, const int pDefault) { if (GetEntity() == nullptr) return pDefault; #ifndef LEADWERKS_5 if (entity->GetFloat(pValue) != NULL) { return static_cast<int>(entity->GetFloat(pValue)); } #else if (GetEntity()->GetNumber(pValue) != NULL) { double x = GetEntity()->GetNumber(pValue); // stored as 54.999999... x = x + 0.5 - (x < 0); // x is now 55.499999... int y = (int)x; // truncated return y; } #endif return pDefault; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void BaseActor::SetStringValue(const std::string& pValue, const std::string& pDefault) { GetEntity()->SetString(pValue, pDefault); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- std::string BaseActor::GetStringValue(const std::string& pValue, const std::string& pDefault) { if (GetEntity() == nullptr) return pDefault; #ifndef LEADWERKS_5 if (entity->GetString(pValue) != "") { return entity->GetString(pValue); } #else if (GetEntity()->GetString(pValue) != "") { return GetEntity()->GetString(pValue); } #endif return pDefault; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- Vec2* BaseActor::GetVec2Value(const std::string& pValue, Vec2* pDefault) { if (GetEntity() == nullptr) return pDefault; #ifndef LEADWERKS_5 Vec2* test = static_cast<Vec2*>(entity->GetObject(pValue)); if (test != NULL) { return test; } #else Vec2* test = (Vec2*)GetEntity()->GetObjectPointer(pValue); if (test != NULL) { return test; } #endif return pDefault; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- Vec3* BaseActor::GetVec3Value(const std::string& pValue, Vec3* pDefault) { if (GetEntity() == nullptr) return pDefault; #ifndef LEADWERKS_5 Vec3* test = static_cast<Vec3*>(entity->GetObject(pValue)); if (test != NULL) { return test; } #else Vec3* test = (Vec3*)GetEntity()->GetObjectPointer(pValue); if (test != NULL) { return test; } #endif return pDefault; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- Vec4* BaseActor::GetVec4Value(const std::string& pValue, Vec4* pDefault) { if (GetEntity() == nullptr) return pDefault; #ifndef LEADWERKS_5 Vec4* test = static_cast<Vec4*>(entity->GetObject(pValue)); if (test != NULL) { return test; } #else Vec4* test = (Vec4*)GetEntity()->GetObjectPointer(pValue); if (test != NULL) { return test; } #endif return pDefault; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- Entity* BaseActor::GetEntityValue(const std::string& pValue, Entity* pDefault) { if (GetEntity() == nullptr) return pDefault; #ifndef LEADWERKS_5 Entity* test = static_cast<Entity*>(entity->GetObject(pValue)); if (test != NULL) { return test; } #else Entity* test = (Entity*)GetEntity()->GetObjectPointer(pValue); if (test != NULL) { return test; } #endif return pDefault; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void BaseActor::FireOutput(const std::string& pEvent) { if (GetEntity() == NULL) return; #ifndef LEADWERKS_5 if (entity->component != NULL) { entity->component->CallOutputs(pEvent); } #else GetEntity()->FireOutputs(pEvent); #endif } Now we need a actor factory of some kind. There is probably an easier and modern way of doing this, but this is how I do it. First we create a header file called actorfactory.h #ifndef ACTORFACTORY_H #define ACTORFACTORY_H #if defined( _WIN32 ) #pragma once #endif #include "stdafx.h" extern void BaseActorFactory(Entity* pEntity, Object* pObject); #endif // ACTORFACTORY_H Then in actorfactory.cpp, I have the following. #include "stdafx.h" #include "baseweapon.h" #include "clientcamera.h" #include "doors.h" #include "noise.h" #include "fpsplayer.h" #include "logicactors.h" #include "pointmessage.h" #include "propspawner.h" #include "point_transition.h" #include "platform.h" #include "triggers.h" #include "vrplayer.h" #define ATTACH_NAME_TO_ACTOR(_name_, _actor_) if (test == _name_) actor = new _actor_() void BaseActorFactory(Entity * pEntity, Object * pObject) { auto classname = GetEntityKeyValue(pEntity, ACTOR_KEYVALUE); if (classname != "") { BaseActor* actor = NULL; std::string entname = pEntity->GetKeyValue("name", "STATIC_MESH"); std::string test = String::Lower(classname); // Global actor is a dummy actor with values for other actors. if (test == "global") { if (g_mGlobalActor == NULL) { actor = new BaseActor(); g_mGlobalActor = actor; } } ATTACH_NAME_TO_ACTOR("ambient_generic", AmbientGeneric); ATTACH_NAME_TO_ACTOR("door_sliding", SlidingDoor); ATTACH_NAME_TO_ACTOR("door_rotating", RotatingDoor); ATTACH_NAME_TO_ACTOR("point_path", PointPath); ATTACH_NAME_TO_ACTOR("point_message", PointMessage); ATTACH_NAME_TO_ACTOR("point_transition", PointTransition); ATTACH_NAME_TO_ACTOR("point_weapon_pickup", PointWeaponPickup); ATTACH_NAME_TO_ACTOR("prop_spawner", PropSpawner); ATTACH_NAME_TO_ACTOR("logic_relay", LogicRelay); ATTACH_NAME_TO_ACTOR("logic_branch", LogicBranch); ATTACH_NAME_TO_ACTOR("logic_counter", LogicCounter); ATTACH_NAME_TO_ACTOR("train", TrainActor); ATTACH_NAME_TO_ACTOR("trigger_once", CollisionTrigger); ATTACH_NAME_TO_ACTOR("trigger_multiple", CollisionTriggerMultiple); ATTACH_NAME_TO_ACTOR("volume", BaseVolume); ATTACH_NAME_TO_ACTOR("volume_push", PushVolume); ATTACH_NAME_TO_ACTOR("volume_hurt", HurtVolume); ATTACH_NAME_TO_ACTOR("weapon_pickup", WeaponPickup); if (BaseActor::AttachActor(pEntity, actor, true)) { pEntity->SetKeyValue(ACTOR_KEYVALUE, classname); System::Print("Attached actor: \"" + classname + "\" to \"" + entname + "\"."); } else { System::Print("Error: failed to attach actor: \"" + classname + "\" to \"" + entname + "\"."); } } } Each actor gets a "classname" assigned to it. This can be anything you want and it's defined here. I went with a quake naming scheme for personal preference. We then call this function in our map load hook as so. This will pass each entity loaded though our factory when the map file is being loaded in. if (Map::Load(pszPath, &BaseActorFactory) == false) { Msg("Failed to load \"" + pszPath + "\" as path is invaild/missing from disk!"); return false; } Custom Actors Now we have the platform to build our actor, lets do so! Here's sample code of a relay actor. I'm picking this class as it shows an input and output functions being used that'll work with the flowgraph as well as loading values. #ifndef LOGICACTORS_H #define LOGICACTORS_H #if defined( _WIN32 ) #pragma once #endif #include "stdafx.h" #include "baseactor.h" class LogicRelay : public BaseActor { long m_intDelayTime; long m_intTriggerTime; public: virtual void Start(); virtual void ReceiveSignal(const std::string& inputname, Entity* sender); virtual void UpdateWorld(); }; #endif #include "stdafx.h" #include "logicactors.h" //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void LogicRelay::Start() { BaseToggle::Start(); m_intDelayTime = GetIntValue("delay", 0); //FireOutput("OnStart"); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void LogicRelay::ReceiveSignal(const std::string& inputname, Entity* sender) { auto _event = String::Lower(inputname); if (_event == "trigger") { m_intTriggerTime = Timing::GetCurrent(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void LogicRelay::UpdateWorld() { if (m_intTriggerTime > 0) { if (Timing::GetCurrent() > m_intTriggerTime + m_intDelayTime) { FireOutput("OnTrigger"); } } } Last we write the lua script: --[[ Purpose: A Logic entity that is used for relaying outputs. ]]-- Script.classname="logic_relay" Script.delay=-0--int "Delay" function Script:Trigger()--in end function Script:Outputs() self.component:CallOutputs("OnStart") self.component:CallOutputs("OnTrigger") end Compile your C++ code and attach your script to your entity. If all is well, you should have a working actor in your game! Remarks This is the best and simple way of getting actors to work in your game much like the lua alternative. However, as a C++ user I can ensure you that your adventure isn't over. First you're going to have to figure out how actor members are deleted and when they should be deleted. The clear function in the world class will release all engine actors, but not assets so you need to take that into consideration. Also, I personally ran into issues with sound Source classes (Now called Speaker in Leadwerks 5) not being recreated when the map was reloaded. I ended up having to make my own source management system. To be honest, I forgot how it works since I haven't really touched my project in months. Even after all my workarounds, I still have things not releasing properly sometimes, but if you're smarter than me, I'm sure you can figure it out. But I wrote this blog so if anyone had questions on how to use actors, You or I can point them here. Happy coding!
  9. You can take a look at the sample menu code for options to see how to obtain all resolutions with lua. Fullscreen doesn't work for everyone for some reason. I had multiple computers run Leadwerks with no issues. The only time I had an issue was when I had that Samsung Magic for SSD motoring running in the background. So I think some apps prevent fullscreen working properly for some reason.
  10. My window code is here that has the solution:
  11. I plan to write something up to give you an idea how to attach actors using the editor.
  12. You need to compile the files in your project. Also, this isn't really an ideal class to use unless your game is mostly C++. I think I'm going write something up for Leadwerks 4 and C++.
  13. Before we were all locked in our homes, I was working on a lot of code for Leadwerks 4 for a reboot of concept I've made back in 2017. While I decided to shelf it again to wait for the new engine (Because the game will run faster, look better, VR, etc) I'm going to share you some of my core code which is cross compatible between the new and old engine. I'm sharing this as I see most people just using stock example code for window creation and setting saving without knowing how it all works. This is more of a show than tell kind of article, and most things are just copy and paste what's in my code at the moment, but it should give you an idea. In this article, I'll be showing you code for both Leadwerks 4 and 5, but most of this was written and tested in Leadwerks 4. Parsing Arguments. Leadwerks 4 has a simple command that does this automatically. This makes it so you can give the app flags on how it should startup/behave on startup. System::ParseCommandLine(argc, argv); In Leadwerks 5, you need to store it in your own std::map. auto commandline = ParseCommandLine(argc, argv); To be honest, the new way is much better. You see, in Leadwerks 4, System::ParseCommandLine() stored the values in a engine defined std::map which is also associated with System::LoadSettings() and System::SaveSettings(). This will cause unwanted results as flags you may only want to happen only when called in a batch file will save to the cfg file saved by default to the AppData folder in Windows. There is a separate map for both Arguments and Settings, but I think the issue is with if you use System::Set/GetProperty(). The best option is to keep your arguments and setting separate. In Leadwerks 5, your std::map is yours, so you're already off to a good start. Actual Settings Now we can take in flags/arguments from launch settings, we now need values to be written on the disk. First we need to build the directory in which will act as our data folder for the user. Where this folder should be is up to you, but it should be in the user's directory on Windows, or the user's home directory in Linux. Here's my code for both Leadwerks 4 and 5. First, we need to know the applications name. We use this name value in multiple areas. Leadwerks 4 as a value System::AppName, but in Leadwerks 5, we'll be creating our own variable. We'll also define the settings directory string path here too. #ifndef LEADWERKS_5 //std::string AppName = "MyGame"; std::string settingsdir = ""; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void App::SetName(const std::string& pszAppName) { System::AppName = pszAppName; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- std::string App::GetName() { return System::AppName; } #else std::wstring AppName = L"MyGame"; std::wstring settingsdir = L""; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void App::SetName(std::wstring pszAppName) { AppName = pszAppName; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- std::wstring App::GetName() { return AppName; } #endif Now we have a name variable, we can use it to build our settings directory. //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void BuildSettingsDirectory() { #ifndef LEADWERKS_5 // If settingsdir is not "", we did this already, no need to do it again. if (settingsdir != "") return; #if defined (_WIN32) auto t = FileSystem::GetDocumentsPath() + "/" + App::GetName(); #else auto t = FileSystem::GetAppDataPath() + "/." + String::Lower(App::GetName()); #endif if (FileSystem::GetFileType(t) == 0) { System::Print("Building Settings folder under:" + t); FileSystem::CreateDir(t); } settingsdir = t; #else // If settingsdir is not "", we did this already, no need to do it again. if (settingsdir != L"") return; #if defined (_WIN32) auto t = FolderLocation(FOLDER_DOCUMENTS) + L"/" + AppName; #else auto t = FolderLocation(FOLDER_APPDATA) + L"/." + Lower(AppName); #endif if (FileType(t) == 0) { Print(L"Building Settings folder under:" + t); CreateDir(t); } settingsdir = t; #endif } You'll now have the path to your settings directory stored in settingsdir. You can use this string to store files there easily like below: (Raw copy and paste, ignore my macros) //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void App::TakeScreenShot() { auto framebuffer = Framebuffer::GetCurrent(); if (framebuffer != NULL) { auto screenshot_path = GetSettingsPath() + "/Screenshots"; if (FileSystem::GetFileType(screenshot_path) == 0) { Msg("Building Screenshots folder under:" + screenshot_path); FileSystem::CreateDir(screenshot_path); } framebuffer->Screenshot(screenshot_path + "/" + App::GetName() + "_" + Timing::GetCurrentDate() + ".tga"); } else { Msg("Error: Failed to take screenshot."); } } A long time ago, @Crazycarpet gave me code to parse a text file like a Leadwerks 4 config file or a Leadwerks 4 mat file. I turned that functionality into a class, and instead of having the engine phrase the information, I do this myself. This is a direct copy and paste of my scopes to give you an idea. // To Load auto settingspath = settingsdir + "/settings.cfg"; if (FileSystem::GetFileType(settingspath) == 0) { appsettings.fullscreen = false; appsettings.screenwidth = 1280; appsettings.screenheight = 720; appsettings.display = 0; appsettings.antialias = 1; //2 appsettings.lightquality = 1; appsettings.terrainquality = 1; appsettings.texturequality = Texture::GetDetail(); appsettings.waterquality = 1; appsettings.afilter = Texture::GetAnisotropy(); appsettings.verticalsync = false; appsettings.hdrmode = true; } else { auto config = LoadConfig(settingspath); appsettings.fullscreen = config->GetBoolValue("fullscreen", false); appsettings.screenwidth = config->GetIntValue("screenwidth", 800); appsettings.screenheight = config->GetIntValue("screenheight", 600); appsettings.display = config->GetIntValue("display", 0); appsettings.antialias = config->GetIntValue("antialias", 1); appsettings.lightquality = config->GetIntValue("lightquality", 1); appsettings.terrainquality = config->GetIntValue("terrainquality", 1); appsettings.texturequality = config->GetIntValue("texturequality", Texture::GetDetail()); appsettings.waterquality = config->GetIntValue("waterquality", 1); appsettings.afilter = config->GetIntValue("afilter", Texture::GetAnisotropy()); appsettings.verticalsync = config->GetBoolValue("verticalsync", false); appsettings.hdrmode = config->GetBoolValue("hdrmode", true); config = NULL; } //---------------------// // To Save auto config = CreateConfig(settingspath); config->WriteKeyValue("fullscreen", to_string(appsettings.fullscreen)); config->WriteKeyValue("screenwidth", to_string(appsettings.screenwidth)); config->WriteKeyValue("screenheight", to_string(appsettings.screenheight)); config->WriteKeyValue("display", to_string(appsettings.display)); config->WriteKeyValue("antialias", to_string(appsettings.antialias)); config->WriteKeyValue("lightquality", to_string(appsettings.lightquality)); config->WriteKeyValue("terrainquality", to_string(appsettings.terrainquality)); config->WriteKeyValue("texturequality", to_string(appsettings.texturequality)); config->WriteKeyValue("waterquality", to_string(appsettings.waterquality)); config->WriteKeyValue("afilter", to_string(appsettings.afilter)); config->WriteKeyValue("tfilter", to_string(appsettings.tfilter)); config->WriteKeyValue("verticalsync", to_string(appsettings.verticalsync)); config->WriteKeyValue("hdrmode", to_string(appsettings.hdrmode)); bool b = config->WriteOut(); config = NULL; return b; Settings vs Arguments Since both settings and arguments are different tables, we must have a way to check if ether the setting or argument is enabled. The best way I chose to do this is to make bool functions that check if ether is true. //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool App::IsFullScreen() { if (appsettings.fullscreen == true || GetArgument("fullscreen") == "1") return true; return appsettings.fullscreen; } Building The Window (Finally) Now it's time to actually build the window with the information we have via settings file, or augments passed in a command line. For Leadwerks 4, I do something like this: Leadwerks::Window* CreateEngineWindow(std::string pszTileName, Leadwerks::Window* parent) { // Are we fullscreen? const bool fullscreenmode = App::IsFullScreen(); // Set the name of the app the same as the window. App::SetName(pszTileName); // Load Settings. App::LoadSettings(); //Load any zip files in main directory Leadwerks::Directory* dir = Leadwerks::FileSystem::LoadDir("."); if (dir) { for (std::size_t i = 0; i < dir->files.size(); i++) { std::string file = dir->files[i]; std::string ext = Leadwerks::String::Lower(Leadwerks::FileSystem::ExtractExt(file)); if (ext == "zip" || ext == "pak") { Leadwerks::Package::Load(file, KEY); } } delete dir; } // Create a window Leadwerks::Window* window; std::string windowtitle = App::GetName(); if (IsDebug()) windowtitle = windowtitle + " [Debug]"; if (fullscreenmode) { iVec2 gfx = System::GetGraphicsMode(Leadwerks::System::CountGraphicsModes() - 1); window = Leadwerks::Window::Create(windowtitle, 0, 0, gfx.x, gfx.y, Leadwerks::Window::Fullscreen, parent); } else { window = Leadwerks::Window::Create(windowtitle, 0, 0, App::GetScreenWidth(), App::GetScreenHeight(), Leadwerks::Window::Titlebar | Leadwerks::Window::Center, parent); } EngineWindow::current = window; window->Activate(); return window; } This is my current Leadwerks 5 code: shared_ptr<Window> CreateEngineWindow(std::wstring pszTileName, shared_ptr<Window> parent) { //Get the primary display auto display = App::GetDisplay(); // Are we fullscreen? const bool fullscreenmode = App::IsFullScreen(); // Set the name of the app the same as the window. App::SetName(pszTileName); // Load Settings. App::LoadSettings(); // Create a window shared_ptr<Window> window; std::wstring windowtitle = App::GetName(); if (IsDebug()) windowtitle = windowtitle + L" [Debug]"; if (fullscreenmode) { auto gfxmodes = display->GraphicsModes(); auto gfx = gfxmodes[gfxmodes.size() - 1]; window = CreateWindow(display, App::GetName(), 0, 0, gfx.x, gfx.y, WINDOW_FULLSCREEN, parent); } else { Vec2 displayscale = display->GetScale(); window = CreateWindow(display, windowtitle, 0, 0, App::GetScreenWidth() * displayscale.x, App::GetScreenHeight() * displayscale.y, WINDOW_TITLEBAR | WINDOW_CENTER, parent); } return window; } As you might have picked up, I store all of the core app functionality in a singleton class called "App". This is what the header looks like to give you an idea. class App { public: #ifndef LEADWERKS_5 static void SetName(const std::string& pszAppName); static std::string GetName(); #else static void SetName(std::wstring pszAppName); static std::wstring GetName(); #endif static void ParseCommandLine(int argc, const char* argv[]); static bool CheckArgument(std::string pszKey); static std::string GetArgument(std::string pszKey); static int GetArgumentInt(std::string pszKey); static float GetArgumentFloat(std::string pszKey); std::map<std::string, std::string> Arguments(); static bool LoadSettings(); static bool SaveSettings(); static AppSettings GetSettings(); #ifndef LEADWERKS_5 static std::string GetSettingsPath(); #else static std::wstring GetSettingsPath(); #endif static void Shutdown(); static bool Running(); static void EnableVR(); static bool IsDevMode(); static bool IsFullScreen(); static unsigned int GetScreenWidth(); static unsigned int GetScreenHeight(); static unsigned int GetDisplayIndex(); #ifdef LEADWERKS_5 static shared_ptr<Display> GetDisplay(); #endif static unsigned int GetAntialias(); static unsigned int GetLightingQuality(); static unsigned int GetTerrainQuality(); static unsigned int GetTextureQuality(); static unsigned int GetWaterQuality(); static unsigned int GetAnisotropicFilter(); static bool GetTrilinearFilter(); static bool GetVerticalSync(); static bool GetHDRMode(); static void TakeScreenShot(); }; Bonus: Load JSON Files Easily Leadwerks 5 includes the json library included, but as of time of writing, there is no simple way to load the format into something usable. Josh shared his approach using the engine's Stream Class. The json library can also be included in Leadwerks 4 with little to no effort! This is a very nice format to have in your base app code. #if defined (EOF) #undef EOF #endif nlohmann::json ReadJSON(const std::string& pszFileName) { #ifndef LEADWERKS_5 auto stream = FileSystem::ReadFile(pszFileName); auto path = FileSystem::RealPath(pszFileName); if (stream == NULL) { Print("Error: Failed to load JSON script \"" + path + "\""); //stream->path = L""; return NULL; } //Check for starting bracket bool started = false; while (!stream->EOF()) { //auto c = stream->ReadLine(); auto c = String::Chr(stream->ReadUChar()); if (String::Trim(c) == "") continue; if (c != "{") return false; started = true; break; } if (!started) return false; stream->Seek(0); #else auto stream = ReadFile(pszFileName); auto path = RealPath(pszFileName); if (stream == NULL) { Print(L"Error: Failed to load JSON script \"" + path + L"\""); //stream->path = L""; return NULL; } //Check for starting bracket bool started = false; while (!stream->Ended()) { auto c = Chr(stream->ReadByte()); if (Trim(c) == "") continue; if (c != "{") return NULL; started = true; break; } if (!started) return false; stream->Seek(0); #endif std::vector<uint8_t> data; auto sz = stream->GetSize(); data.resize(sz); stream->Read(data.data(), sz); nlohmann::json j3; try { j3 = nlohmann::json::parse(data); } catch (std::exception & e) { std::cout << e.what() << std::endl; return false; } if (j3.type() != nlohmann::json::value_t::object) return NULL; #ifndef LEADWERKS_5 stream->Release(); #endif stream = NULL; return j3; } #ifdef LEADWERKS_5 nlohmann::json WReadJSON(const std::wstring& pszFileName) { auto stream = ReadFile(pszFileName); auto path = RealPath(pszFileName); if (stream == NULL) { Print(L"Error: Failed to load JSON script \"" + path + L"\""); //stream->path = L""; return NULL; } //Check for starting bracket bool started = false; while (!stream->Ended()) { auto c = Chr(stream->ReadByte()); if (Trim(c) == "") continue; if (c != "{") return NULL; started = true; break; } if (!started) return false; stream->Seek(0); std::vector<uint8_t> data; auto sz = stream->GetSize(); data.resize(sz); stream->Read(data.data(), sz); nlohmann::json j3; try { j3 = nlohmann::json::parse(data); } catch (std::exception & e) { std::cout << e.what() << std::endl; return false; } if (j3.type() != nlohmann::json::value_t::object) return NULL; stream = NULL; return j3; } #endif This type of code are things you can do right now in the Leadwerks 5 beta while you wait for the rest of the engine to come online. As for me, I already have this code done and this layer of stuff works flawlessly and produces the same results on both engines. If there is anything else you want me to share based on core.
  14. reepblue


    My great aunt was good at this kind of stuff. I don't know about these, but what she did in the past involved careful cuts with an exacto knife. I lack the hand coordination to do these things sadly.
  15. The only functions you'll need the Leadwerks namespace in front of the call is Time and Window. Make a precompiled/shared header and define the following. #ifndef STDAFX_H #define STDAFX_H #if defined( _WIN32 ) #pragma once #endif #include "Leadwerks.h" #ifdef GetFileType #undef GetFileType #endif using namespace Leadwerks; #define Timing Leadwerks::Time #define EngineWindow Leadwerks::Window #endif // STDAFX_H Then whenever you want to use the Time or Window function you'll just call something like this: #include "pch.h" int main() { auto w = EngineWindow::Create(); while (true) { Timing::Update(); } } This ensures that your code will be cross compatible and you don't have to ever worry about using the namespace.
  • Create New...