Jump to content

Josh

Staff
  • Posts

    23,139
  • Joined

  • Last visited

Everything posted by Josh

  1. I put together the first editor extension using a Lua module. LuaModule.zip Your main.c file needs one important function. The name of the module is "LuaZenMode", so the DLL needs to be called "LuaZenMode.dll" and the function needs to be called "luaopen_LuaZenMode". You will get an error if you call both the script and the module the same thing, because Lua will think the script is trying to load itself. __declspec(dllexport) int luaopen_LuaZenMode(lua_State* L) { lua_pushcfunction(L, SetWindowZenMode); return 1; } In this case I am just assigning a function to the return value, but in most cases you probably want to create a table and add the function pointers to the table. Here is our one function the lib uses: static int SetWindowZenMode(lua_State* L) { HWND hwnd = (HWND)luaL_checkinteger(L, 1); int state = luaL_checkint(L, 2); if (state == 1) { SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~(WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME)); } else { SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME); } return 0; } It's okay to treat the HWND as a Lua number because even 64-bit Windows uses 32-bit window handles. https://learn.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication?redirectedfrom=MSDN Here is how I load it in my script named ZenMode.lua: local extension = {} extension.func = require "LuaZenMode" The Lua code then creates a menu divider and a new menu item: local menu = program.menu:FindChild("View", false) if menu == nil then Print("Error: ZenMode extension cannot find View menu") return end --Add divider CreateMenu("", menu) --Add menu item extension.menu = CreateMenu("Zen Mode", menu) And then it declares a function and uses that as an event listener. Note that the extension table is being passed in the extra function parameter: function extension.ProcessEvent(event, extra) Print("Calling ProcessEvent...") local hwnd = program.window:GetHandle() if extra.menu:GetState() == 0 then Print("Enabling Zen mode...") extra.menu:SetState(1) extra.windowposition = {} extra.windowsize = {} extra.windowposition.x = program.window.position.x extra.windowposition.y = program.window.position.y extra.windowsize.x = program.window.size.x extra.windowsize.y = program.window.size.y program.window:SetShape(program.window.display.position.x, program.window.display.position.y, program.window.display.size.x, program.window.display.size.y) extra.func(hwnd, 1) Print("Zen mode enabled") else Print("Disabling Zen mode...") extra.menu:SetState(0) extra.func(hwnd, 0) program.window:SetShape(extra.windowposition.x, extra.windowposition.y, extra.windowsize.x, extra.windowsize.y) Print("Zen mode disabled") end end ListenEvent(EVENT_WIDGETACTION, extension.menu, extension.ProcessEvent, extension) And now the program has new functionality added entirely by the extension.
  2. Josh

    Zen Mode

    Full-screen no distraction mode: auto hwnd = window->GetHandle(); SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) &~ (WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME)); window->SetShape(0, 0, window->display->size.x, window->display->size.y);
  3. Yeah, I did a lot of work on the scroll bars today. The next update will have a lot of small GUI fixes. I also figured out how to get things to be really pixel-perfect at every scaling factor.
  4. I would never be able to fine-tune the interface like this using a third-party GUI library.
  5. More fine-tuning of the interface...
  6. Some things can't be improved...
  7. Got rid of the viewport toolbars, added a new tabber style, and made the menu and status bars span the entire window. This takes some space away from the side panel, but it makes it easier to recognize the program layout.
  8. Maybe it would be good to have regular "entity" mode editing, how Leadwerks and 3ds Max treat models, and then have a CSG mode you switch into for brush editing. In entity mode brushes would get treated like a model does, with the position / rotation / scale widget for control
  9. Yeah, so far I find TrenchBroom to e pretty confusing. It's a bit of a guessing game what will happen when you move the mouse. But there may be some good ideas in it.
  10. And this is the code to get the start menu location to place a link / shortcut in: PIDLIST_ABSOLUTE pidlist; wchar_t path[MAX_PATH]; HRESULT hr = SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAMS, &pidlist); BOOL b = SHGetPathFromIDListW(pidlist, path); WString s = WString(path); Print(s);
  11. Weird. It works if I convert the wide strings to Utf8: bool CreateShortcut(const WString& path, const WString& dest, const WString& desc) { auto rpath = RealPath(path).Replace("/", "\\"); auto rdest = RealPath(dest).Replace("/", "\\"); String s1 = rpath.ToUtf8String(); String s2 = desc.ToUtf8String(); String s3 = ExtractDir(rpath).Replace("/","\\").ToUtf8String(); LPCSTR lpszPathObj = s1.c_str(); LPCWSTR lpszPathLink = rdest.c_str(); LPCSTR lpszDesc = s2.c_str(); CoInitialize(NULL); HRESULT hres; IShellLink* psl; hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); if (not SUCCEEDED(hres)) return false; IPersistFile* ppf; psl->SetPath(lpszPathObj); psl->SetWorkingDirectory(s3.c_str()); psl->SetDescription(lpszDesc); hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); if (SUCCEEDED(hres)) { hres = ppf->Save(lpszPathLink, TRUE); ppf->Release(); } psl->Release(); return hres == S_OK; }
  12. My string values in the shortcut file are getting terminated after the first character. Can anyone see the error? #include "windows.h" #include "winnls.h" #include "shobjidl.h" #include "objbase.h" #include "objidl.h" #include "shlguid.h" // https://learn.microsoft.com/en-au/windows/win32/shell/links?redirectedfrom=MSDN#creating-a-shortcut-and-a-folder-shortcut-to-a-file bool CreateShortcut(const WString& path, const WString& dest, const WString& desc) { auto rpath = RealPath(path).Replace("/", "\\"); auto rdest = RealPath(dest).Replace("/", "\\"); LPCWSTR lpszPathObj = rpath.c_str(); LPCWSTR lpszPathLink = rdest.c_str(); LPCWSTR lpszDesc = desc.c_str(); CoInitialize(NULL); HRESULT hres; IShellLinkW* psl; hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); if (SUCCEEDED(hres)) { IPersistFile* ppf; psl->SetPath(lpszPathObj); psl->SetDescription(lpszDesc); hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); if (SUCCEEDED(hres)) { hres = ppf->Save(lpszPathLink, TRUE); ppf->Release(); } psl->Release(); } return hres == S_OK; } // Example: CreateShortcut("Ultra Engine.exe", GetPath(PATH_DESKTOP) + "/Ultra Engine.lnk", "Description");
  13. There is a program called "Trenchbroom" that is popular in Quake modding now. It has some nice ideas for 3D CSG editing.
  14. Although it follows the same basic idea, there are a lot of differences between this and Leadwerks. In Leadwerks it was all about locking assets into one exact defined format, and Ultra is a lot more free-wheeling in this regard. This made the workflow for Ultra much harder to program and design, but the result seems very nice and powerful. With the rest of the editor, I don't think a lot of big changes are going to be needed. I think the rest will not take that long to write.
  15. I'm going to make it so if you hold the alt key when you click on a model, it selects the material.
  16. Josh

    Selection Highlight

    I had no idea the interior walls looked like that. I just never really thought about it.
  17. Josh

    Selection Highlight

    I like this because it communicates a ton of information at a glance.
  18. This shows how to render specific objects to another texture and display an outline around them: #include "UltraEngine.h" #include "ComponentSystem.h" using namespace UltraEngine; int main(int argc, const char* argv[]) { //Get the displays auto displays = GetDisplays(); //Create a window auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR); //Create a world auto world = CreateWorld(); //Create a framebuffer auto framebuffer = CreateFramebuffer(window); //Create a camera auto camera = CreateCamera(world); camera->SetClearColor(0.125); camera->SetFov(70); camera->SetPosition(0, 0, -3); //Create a light auto light = CreateBoxLight(world); light->SetRotation(35, 45, 0); light->SetRange(-10, 10); //Create a box auto box = CreateBox(world); //Entity component system auto actor = CreateActor(box); auto component = actor->AddComponent<Mover>(); component->rotation.y = 45; //------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------ //Render to texture box->SetRenderLayers(1 + 2); auto cam2 = CreateCamera(world); cam2->SetClearColor(0, 0, 0, 0); cam2->SetRenderLayers(2); cam2->SetFov(camera->GetFov()); cam2->SetMatrix(camera->matrix); cam2->AddPostEffect(LoadPostEffect("Shaders/PostEffects/Outline.json")); auto sz = framebuffer->GetSize(); auto texbuffer = CreateTextureBuffer(sz.x, sz.y); cam2->SetRenderTarget(texbuffer); //Display overlay auto sprite = CreateSprite(world, sz.x, sz.y); sprite->SetColor(0, 1, 1, 1); sprite->SetRenderLayers(4); auto mtl = CreateMaterial(); sprite->SetMaterial(mtl); mtl->SetTransparent(true); mtl->SetTexture(texbuffer->GetColorAttachment()); auto cam3 = CreateCamera(world, PROJECTION_ORTHOGRAPHIC); cam3->SetClearMode(CLEAR_DEPTH); cam3->SetRenderLayers(4); cam3->SetPosition(sz.x * 0.5f, sz.y * 0.5f, 0); //------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------ //Main loop while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false) { world->Update(); world->Render(framebuffer); } return 0; }
  19. Updated 1.0.2 Render layers now ignore hierarchy, i.e. a parent's setting does not affect the child Added Mesh::SetRenderLayers() (default is all layers) Removed Entity:SetSelected / GetSelected
  20. UltraEngine_c.exe is updated, and the required post-processing effect is added. Sync your project to get the files. This will highlight select limbs and meshes in blue, and when a material node is selected in the tree, all meshes that use that material will be highlighted with yellow. The outline effect won't work in your game without a little extra setup. I'll add an example to show how.
  21. I'm using the render layers to draw the selected objects to a separate camera that is rendering to a texture, with a post-processing effect that highlights edges based on depth being less than 1.0. Then that image is applied to a sprite that appears on top of the scene, and viola, selected objects are highlighted. It uses three cameras, one for the main scene, one to render the selected objects, and then another for to draw the sprite with orthographic projection on top of the scene.
  22. I'm working out a better approach to the selection highlight. This shows the edge outline on top of other objects, while the original object stays underneath in the correct Z-order. I think they did this in Left 4 Dead to show your teammate's outline through walls. This is possible without too much trouble due to the wonderful multi-camera / post-processing / 2D rendering system.
×
×
  • Create New...