This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
licensing:samplecode [2009-07-05 11:44] 127.0.0.1 external edit |
— (current) | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Cafu C++ Sample Source Code ====== | ||
- | |||
- | This page contains several sections of C++ source code from the Cafu engine. | ||
- | For more ways to evaluate and getting an impression of Cafu, please refer to [[licensing:faqs#can_i_preview_and_evaluate_cafu|this Licensing FAQ]]. | ||
- | |||
- | |||
- | ===== Libs/GuiSys/GuiImpl.cpp ===== | ||
- | |||
- | This file is part of the Cafu GUI System. It implements a single GUI as e.g. the games Main Menu or a panel on a world entity. | ||
- | |||
- | <code cpp> | ||
- | #include "GuiImpl.hpp" | ||
- | #include "GuiMan.hpp" | ||
- | |||
- | |||
- | using namespace cf::GuiSys; | ||
- | |||
- | |||
- | GuiImplT::GuiImplT(const std::string& GuiScriptName, bool IsInlineCode) | ||
- | : ScriptName(IsInlineCode ? "" : GuiScriptName), | ||
- | LuaState(NULL), | ||
- | RootWindow(NULL), | ||
- | FocusWindow(NULL), | ||
- | MouseOverWindow(NULL), | ||
- | IsActive(true), | ||
- | IsInteractive(true), | ||
- | IsFullCover(false), | ||
- | MousePosX(VIRTUAL_SCREEN_SIZE_X/2.0f), // 320.0f | ||
- | MousePosY(VIRTUAL_SCREEN_SIZE_Y/2.0f), // 240.0f | ||
- | MouseIsShown(true) | ||
- | { | ||
- | // Initialize Lua. | ||
- | LuaState=lua_open(); | ||
- | |||
- | lua_pushcfunction(LuaState, luaopen_base); lua_pushstring(LuaState, ""); lua_call(LuaState, 1, 0); // Opens the basic library. | ||
- | lua_pushcfunction(LuaState, luaopen_package); lua_pushstring(LuaState, LUA_LOADLIBNAME); lua_call(LuaState, 1, 0); // Opens the package library. | ||
- | lua_pushcfunction(LuaState, luaopen_table); lua_pushstring(LuaState, LUA_TABLIBNAME); lua_call(LuaState, 1, 0); // Opens the table library. | ||
- | lua_pushcfunction(LuaState, luaopen_io); lua_pushstring(LuaState, LUA_IOLIBNAME); lua_call(LuaState, 1, 0); // Opens the I/O library. | ||
- | lua_pushcfunction(LuaState, luaopen_os); lua_pushstring(LuaState, LUA_OSLIBNAME); lua_call(LuaState, 1, 0); // Opens the OS library. | ||
- | lua_pushcfunction(LuaState, luaopen_string); lua_pushstring(LuaState, LUA_STRLIBNAME); lua_call(LuaState, 1, 0); // Opens the string lib. | ||
- | lua_pushcfunction(LuaState, luaopen_math); lua_pushstring(LuaState, LUA_MATHLIBNAME); lua_call(LuaState, 1, 0); // Opens the math lib. | ||
- | |||
- | // Load the console library. (Adds a global table with name "Console" to the LuaState with the functions of the ConsoleI interface.) | ||
- | cf::ConsoleI::RegisterLua(LuaState); | ||
- | |||
- | // Load the "ci" (console interpreter) library. (Adds a global table with name "ci" to the LuaState with (some of) the functions of the ConsoleInterpreterI interface.) | ||
- | ConsoleInterpreterI::RegisterLua(LuaState); | ||
- | |||
- | // Adds a global (meta-)table with methods for cf::GuiSys::GuiTs to the LuaState, to be used as metatable for userdata of type cf::GuiSys::GuiT. | ||
- | GuiImplT::RegisterLua(LuaState); | ||
- | |||
- | |||
- | // For each (window-)class that the TypeInfoMan knows about, add a (meta-)table to the registry of the LuaState. | ||
- | // The (meta-)table holds the Lua methods that the respective class implements in C++ and is to be used as metatable for instances of this class. | ||
- | cf::TypeSys::TypeInfoManT& TIM=GetWindowTIM(); | ||
- | |||
- | // ANALOGY: This code is IDENTICAL WITH that in Games/DeathMatch/Code/ScriptState.cpp. | ||
- | for (unsigned long RootNr=0; RootNr<TIM.GetTypeInfoRoots().Size(); RootNr++) | ||
- | { | ||
- | for (const cf::TypeSys::TypeInfoT* TI=TIM.GetTypeInfoRoots()[RootNr]; TI!=NULL; TI=TI->GetNext()) | ||
- | { | ||
- | assert(lua_gettop(LuaState)==0); | ||
- | |||
- | // Create a new table T and add it into the registry table with TI->ClassName (e.g. "cf::GuiSys::WindowT") as the key and T as the value. | ||
- | // This also leaves T on top of the stack. See PiL2 chapter 28.2 for more details. | ||
- | luaL_newmetatable(LuaState, TI->ClassName); | ||
- | |||
- | // See PiL2 chapter 28.3 for a great explanation on what is going on here. | ||
- | // Essentially, we set T.__index = T (the luaL_newmetatable() function left T on the top of the stack). | ||
- | lua_pushvalue(LuaState, -1); // Pushes/duplicates the new table T on the stack. | ||
- | lua_setfield(LuaState, -2, "__index"); // T.__index = T; | ||
- | |||
- | // Now insert the functions listed in TI->MethodsList into T (the table on top of the stack). | ||
- | if (TI->MethodsList!=NULL) | ||
- | luaL_register(LuaState, NULL, TI->MethodsList); | ||
- | |||
- | // If TI has a base class, model that relationship for T, too, by setting the metatable of the base class as the metatable for T. | ||
- | // Note that this works because the for-loop (over TI) enumerates the base classes always before their child classes! | ||
- | if (TI->Base) | ||
- | { | ||
- | assert(strcmp(TI->BaseClassName, TI->Base->ClassName)==0); | ||
- | |||
- | // Get the metatable M with name (key) TI->Base->ClassName (e.g. "cf::GameSys::BaseEntityT") | ||
- | // from the registry, and set it as metatable of T. | ||
- | luaL_getmetatable(LuaState, TI->Base->ClassName); | ||
- | lua_setmetatable(LuaState, -2); | ||
- | } | ||
- | |||
- | // Clear the stack. | ||
- | assert(lua_gettop(LuaState)==1); | ||
- | lua_pop(LuaState, 1); | ||
- | } | ||
- | } | ||
- | |||
- | |||
- | // Add a global variable with name "gui" to the Lua state. "gui" is a table that scripts can use to call GUI methods. | ||
- | { | ||
- | assert(lua_gettop(LuaState)==0); | ||
- | |||
- | // Stack indices of the table and userdata that we create. | ||
- | const int USERDATA_INDEX=2; | ||
- | const int TABLE_INDEX =1; | ||
- | |||
- | // Create a new table T, which is pushed on the stack and thus at stack index TABLE_INDEX. | ||
- | lua_newtable(LuaState); | ||
- | |||
- | // Create a new user datum UD, which is pushed on the stack and thus at stack index USERDATA_INDEX. | ||
- | GuiImplT** UserData=(GuiImplT**)lua_newuserdata(LuaState, sizeof(GuiImplT*)); | ||
- | |||
- | // Initialize the memory allocated by the lua_newuserdata() function. | ||
- | *UserData=this; | ||
- | |||
- | // T["__userdata_cf"] = UD | ||
- | lua_pushvalue(LuaState, USERDATA_INDEX); // Duplicate the userdata on top of the stack (as the argument for lua_setfield()). | ||
- | lua_setfield(LuaState, TABLE_INDEX, "__userdata_cf"); | ||
- | |||
- | // Get the table with name (key) "cf::GuiSys::GuiT" from the registry, | ||
- | // and set it as metatable of the newly created table. | ||
- | luaL_getmetatable(LuaState, "cf::GuiSys::GuiT"); | ||
- | lua_setmetatable(LuaState, TABLE_INDEX); | ||
- | |||
- | // Get the table with name (key) "cf::GuiSys::GuiT" from the registry, | ||
- | // and set it as metatable of the newly created user data (for user data type safety, see PiL2, chapter 28.2). | ||
- | luaL_getmetatable(LuaState, "cf::GuiSys::GuiT"); | ||
- | lua_setmetatable(LuaState, USERDATA_INDEX); | ||
- | |||
- | // Remove UD from the stack, so that only the new table T is left on top of the stack. | ||
- | // Then add it as a global variable whose name is "gui". | ||
- | // As lua_setglobal() pops the table from the stack, the stack is left empty. | ||
- | lua_pop(LuaState, 1); | ||
- | lua_setglobal(LuaState, "gui"); | ||
- | // Could instead do lua_setfield(LuaState, LUA_REGISTRYINDEX, "gui"); as well, | ||
- | // so that script methods like "new()" and "thread()" could also be called without the "gui:" prefix. | ||
- | } | ||
- | |||
- | // Make sure that everyone dealt properly with the Lua stack so far. | ||
- | assert(lua_gettop(LuaState)==0); | ||
- | |||
- | |||
- | // Add a table with name "__pending_coroutines_cf" to the registry. | ||
- | // This table will be used to keep track of the pending coroutines, making sure that Lua doesn't garbage collect them early. | ||
- | lua_newtable(LuaState); | ||
- | lua_setfield(LuaState, LUA_REGISTRYINDEX, "__pending_coroutines_cf"); | ||
- | |||
- | |||
- | // Run the equivalent to "wait=coroutine.yield;" and "waitFrame=coroutine.yield;", that is, | ||
- | // provide aliases for coroutine.yield as known from Doom3 map scripting. | ||
- | lua_getglobal(LuaState, "coroutine"); | ||
- | lua_getfield(LuaState, -1, "yield"); | ||
- | lua_setglobal(LuaState, "wait"); | ||
- | lua_getfield(LuaState, -1, "yield"); | ||
- | lua_setglobal(LuaState, "waitFrame"); | ||
- | lua_pop(LuaState, 1); | ||
- | |||
- | |||
- | // Load the user script! | ||
- | const int LoadResult=IsInlineCode ? luaL_loadstring(LuaState, GuiScriptName.c_str()) | ||
- | : luaL_loadfile (LuaState, GuiScriptName.c_str()); | ||
- | |||
- | if (LoadResult!=0 || lua_pcall(LuaState, 0, 0, 0)!=0) | ||
- | { | ||
- | Console->Warning(std::string("Lua script \"")+GuiScriptName+"\" could not be loaded\n"); | ||
- | Console->Print(std::string("(")+lua_tostring(LuaState, -1)+").\n"); | ||
- | lua_pop(LuaState, 1); | ||
- | } | ||
- | |||
- | // Make sure that everyone dealt properly with the Lua stack so far. | ||
- | assert(lua_gettop(LuaState)==0); | ||
- | |||
- | |||
- | if (RootWindow==NULL) | ||
- | { | ||
- | Console->Warning("No root window set for GUI \""+GuiScriptName+"\"!\n"); | ||
- | |||
- | // Note that just running some Lua code like "gui:SetRootWindow(gui:new('WindowT'));" | ||
- | // here in order to save the situation is not as easy as it seems, because running | ||
- | // this code is not guaranteed to be fail-safe and thus not guaranteed to fix the | ||
- | // problem. That is, there might still be a case left where we might want to throw. | ||
- | lua_close(LuaState); | ||
- | throw InitErrorT(); | ||
- | } | ||
- | |||
- | |||
- | // Finally call the Lua OnInit() method of each window. | ||
- | ArrayT<WindowT*> AllChildren; | ||
- | |||
- | AllChildren.PushBack(RootWindow.GetRaw()); | ||
- | RootWindow->GetChildren(AllChildren, true); | ||
- | |||
- | for (unsigned long ChildNr=0; ChildNr<AllChildren.Size(); ChildNr++) | ||
- | { | ||
- | AllChildren[ChildNr]->CallLuaMethod("OnInit"); | ||
- | } | ||
- | |||
- | |||
- | // Make sure that everyone dealt properly with the Lua stack so far. | ||
- | assert(lua_gettop(LuaState)==0); | ||
- | } | ||
- | |||
- | |||
- | const std::string& GuiImplT::GetScriptName() const | ||
- | { | ||
- | return ScriptName; | ||
- | } | ||
- | |||
- | |||
- | void GuiImplT::Activate(bool doActivate) | ||
- | { | ||
- | IsActive=doActivate; | ||
- | |||
- | // Call the OnActivate() or OnDeactivate() methods of all windows. | ||
- | ArrayT<WindowT*> AllChildren; | ||
- | |||
- | AllChildren.PushBack(RootWindow.GetRaw()); | ||
- | RootWindow->GetChildren(AllChildren, true); | ||
- | |||
- | for (unsigned long ChildNr=0; ChildNr<AllChildren.Size(); ChildNr++) | ||
- | AllChildren[ChildNr]->CallLuaMethod(IsActive ? "OnActivate" : "OnDeactivate"); | ||
- | } | ||
- | |||
- | |||
- | void GuiImplT::SetInteractive(bool IsInteractive_) | ||
- | { | ||
- | IsInteractive=IsInteractive_; | ||
- | } | ||
- | |||
- | |||
- | void GuiImplT::SetMousePos(float MousePosX_, float MousePosY_) | ||
- | { | ||
- | MousePosX=MousePosX_; | ||
- | MousePosY=MousePosY_; | ||
- | |||
- | |||
- | // Clip the mouse position to valid coordinates. | ||
- | if (MousePosX<0.0f) MousePosX=0.0; | ||
- | if (MousePosX>VIRTUAL_SCREEN_SIZE_X) MousePosX=VIRTUAL_SCREEN_SIZE_X; | ||
- | |||
- | if (MousePosY<0.0f) MousePosY=0.0; | ||
- | if (MousePosY>VIRTUAL_SCREEN_SIZE_Y) MousePosY=VIRTUAL_SCREEN_SIZE_Y; | ||
- | |||
- | |||
- | // Determine if the mouse cursor has been moved into (or "over") another window, | ||
- | // that is, see if we have to run any OnMouseLeave() and OnMouseEnter() scripts. | ||
- | WindowT* Win=RootWindow->Find(MousePosX, MousePosY); | ||
- | |||
- | if (Win!=MouseOverWindow.GetRaw()) | ||
- | { | ||
- | if (MouseOverWindow!=NULL) MouseOverWindow->CallLuaMethod("OnMouseLeave"); | ||
- | MouseOverWindow=Win; | ||
- | if (MouseOverWindow!=NULL) MouseOverWindow->CallLuaMethod("OnMouseEnter"); | ||
- | } | ||
- | } | ||
- | |||
- | |||
- | void GuiImplT::SetShowMouse(bool ShowMouse_) | ||
- | { | ||
- | MouseIsShown=ShowMouse_; | ||
- | } | ||
- | |||
- | |||
- | void GuiImplT::Render() | ||
- | { | ||
- | RootWindow->Render(); | ||
- | |||
- | if (MouseIsShown) | ||
- | { | ||
- | // Render the background. | ||
- | // MatSys::Renderer->SetCurrentAmbientLightColor(BackColor); | ||
- | MatSys::Renderer->SetCurrentMaterial(GuiMan->GetDefaultRM() /*BackMaterial*/); | ||
- | |||
- | static MatSys::MeshT CrossMesh(MatSys::MeshT::Lines); | ||
- | CrossMesh.Vertices.Overwrite(); | ||
- | CrossMesh.Vertices.PushBackEmpty(4); // Just a single quad for the background rectangle. | ||
- | |||
- | for (unsigned long VertexNr=0; VertexNr<CrossMesh.Vertices.Size(); VertexNr++) | ||
- | { | ||
- | CrossMesh.Vertices[VertexNr].Color[0]=0.8f; | ||
- | CrossMesh.Vertices[VertexNr].Color[1]=0.0f; | ||
- | CrossMesh.Vertices[VertexNr].Color[2]=0.0f; | ||
- | CrossMesh.Vertices[VertexNr].Color[3]=1.0f; | ||
- | } | ||
- | |||
- | const float b=10.0f; // The cursor size. | ||
- | |||
- | // The tex-coord currently don't make much sense... | ||
- | CrossMesh.Vertices[0].SetOrigin(MousePosX-b, MousePosY ); CrossMesh.Vertices[0].SetTextureCoord(0.0f, 0.0f); | ||
- | CrossMesh.Vertices[1].SetOrigin(MousePosX+b, MousePosY ); CrossMesh.Vertices[1].SetTextureCoord(1.0f, 0.0f); | ||
- | CrossMesh.Vertices[2].SetOrigin(MousePosX, MousePosY-b); CrossMesh.Vertices[2].SetTextureCoord(1.0f, 1.0f); | ||
- | CrossMesh.Vertices[3].SetOrigin(MousePosX, MousePosY+b); CrossMesh.Vertices[3].SetTextureCoord(0.0f, 1.0f); | ||
- | |||
- | MatSys::Renderer->RenderMesh(CrossMesh); | ||
- | } | ||
- | } | ||
- | |||
- | |||
- | bool GuiImplT::ProcessDeviceEvent(const CaKeyboardEventT& KE) | ||
- | { | ||
- | for (WindowT* Win=FocusWindow.GetRaw(); Win!=NULL; Win=Win->GetParent()) | ||
- | { | ||
- | bool KeyWasProcessed=false; | ||
- | bool ResultOK =false; | ||
- | |||
- | switch (KE.Type) | ||
- | { | ||
- | case CaKeyboardEventT::CKE_KEYDOWN: ResultOK=Win->CallLuaMethod("OnKeyPress", "i>b", KE.Key, &KeyWasProcessed); break; | ||
- | case CaKeyboardEventT::CKE_CHAR: ResultOK=Win->CallLuaMethod("OnChar", "i>b", KE.Key, &KeyWasProcessed); break; | ||
- | case CaKeyboardEventT::CKE_KEYUP: ResultOK=Win->CallLuaMethod("OnKeyRelease", "i>b", KE.Key, &KeyWasProcessed); break; | ||
- | } | ||
- | |||
- | if (ResultOK && KeyWasProcessed) return true; | ||
- | if (Win->OnInputEvent(KE)) return true; | ||
- | } | ||
- | |||
- | return false; | ||
- | } | ||
- | |||
- | |||
- | bool GuiImplT::ProcessDeviceEvent(const CaMouseEventT& ME) | ||
- | { | ||
- | // Note that the processing of the mouse event is orthogonal to (independent of) MouseIsShown. | ||
- | // That is, we may be active and interactive even if the mouse cursor is *not* shown, | ||
- | // as for example with the 3D window of the Cafu engine client, which must receive the | ||
- | // mouse events even if no mouse cursor is shown! | ||
- | |||
- | bool MEWasProcessed=false; // If the Lua script handler consumed the event. | ||
- | bool ResultOK =false; // If the Lua script handler returned without script error. | ||
- | |||
- | switch (ME.Type) | ||
- | { | ||
- | case CaMouseEventT::CM_BUTTON0: | ||
- | case CaMouseEventT::CM_BUTTON1: | ||
- | case CaMouseEventT::CM_BUTTON2: | ||
- | case CaMouseEventT::CM_BUTTON3: | ||
- | { | ||
- | const bool ButtonDown=(ME.Amount>0); // Was it a "button up" or "button down" event? | ||
- | |||
- | if (MouseOverWindow!=NULL) | ||
- | { | ||
- | ResultOK=MouseOverWindow->CallLuaMethod(ButtonDown ? "OnMouseButtonDown" : "OnMouseButtonUp", "i>b", ME.Type, &MEWasProcessed); | ||
- | } | ||
- | |||
- | // Change the keyboard input focus, but only if the mouse button event was not handled by the script | ||
- | // (by handling the event the script signals that the default behaviour (focus change) should not take place). | ||
- | if (!MEWasProcessed && ME.Type==CaMouseEventT::CM_BUTTON0 && ButtonDown && MouseOverWindow!=FocusWindow.GetRaw()) | ||
- | { | ||
- | // Only "unhandled left mouse button down" events change the keyboard input focus. | ||
- | if (FocusWindow!=NULL) FocusWindow->CallLuaMethod("OnFocusLose"); | ||
- | FocusWindow=MouseOverWindow; | ||
- | if (FocusWindow!=NULL) FocusWindow->CallLuaMethod("OnFocusGain"); | ||
- | } | ||
- | |||
- | break; | ||
- | } | ||
- | |||
- | case CaMouseEventT::CM_MOVE_X: | ||
- | { | ||
- | // Update the mouse cursor position according to the ME. | ||
- | SetMousePos(MousePosX+ME.Amount, MousePosY); | ||
- | |||
- | // if (MouseOverWindow!=NULL) | ||
- | // { | ||
- | // ResultOK=MouseOverWindow->CallLuaMethod("OnMouseMove", "ii>b", RelMousePosX, RelMousePosY, &MEWasProcessed); | ||
- | // } | ||
- | break; | ||
- | } | ||
- | |||
- | case CaMouseEventT::CM_MOVE_Y: | ||
- | { | ||
- | // Update the mouse cursor position according to the ME. | ||
- | SetMousePos(MousePosX, MousePosY+ME.Amount); | ||
- | |||
- | // if (MouseOverWindow!=NULL) | ||
- | // { | ||
- | // ResultOK=MouseOverWindow->CallLuaMethod("OnMouseMove", "ii>b", RelMousePosX, RelMousePosY, &MEWasProcessed); | ||
- | // } | ||
- | break; | ||
- | } | ||
- | |||
- | default: | ||
- | // Just ignore the other possible ME types. | ||
- | break; | ||
- | } | ||
- | |||
- | if (ResultOK && MEWasProcessed) return true; | ||
- | if (MouseOverWindow==NULL) return false; | ||
- | |||
- | float AbsWinPosX; | ||
- | float AbsWinPosY; | ||
- | |||
- | MouseOverWindow->GetAbsolutePos(AbsWinPosX, AbsWinPosY); | ||
- | |||
- | return MouseOverWindow->OnInputEvent(ME, MousePosX-AbsWinPosX, MousePosY-AbsWinPosY); | ||
- | } | ||
- | |||
- | |||
- | void GuiImplT::DistributeClockTickEvents(float t) | ||
- | { | ||
- | ArrayT<WindowT*> AllChildren; | ||
- | |||
- | AllChildren.PushBack(RootWindow.GetRaw()); | ||
- | RootWindow->GetChildren(AllChildren, true); | ||
- | |||
- | for (unsigned long ChildNr=0; ChildNr<AllChildren.Size(); ChildNr++) | ||
- | { | ||
- | AllChildren[ChildNr]->OnClockTickEvent(t); | ||
- | AllChildren[ChildNr]->CallLuaMethod("OnFrame"); | ||
- | } | ||
- | |||
- | RunPendingCoroutines(t); | ||
- | } | ||
- | |||
- | |||
- | bool GuiImplT::CallLuaFunc(const char* FuncName, const char* Signature, ...) | ||
- | { | ||
- | // Note that when re-entrancy occurs, we do usually NOT have an empty stack here! | ||
- | // That is, when we first call a Lua function the stack is empty, but when the called Lua function | ||
- | // in turn calls back into our C++ code (e.g. a console function), and the C++ code in turn gets here, | ||
- | // we have a case of re-entrancy and the stack is not empty! | ||
- | // That is, the assert() statement in the next line does not generally hold. | ||
- | // assert(lua_gettop(LuaState)==0); | ||
- | |||
- | // Get the desired global function. | ||
- | lua_getglobal(LuaState, FuncName); | ||
- | |||
- | if (!lua_isfunction(LuaState, -1)) | ||
- | { | ||
- | // If we get here, this usually means that the value at -1 is just nil, i.e. the | ||
- | // function that we would like to call was just not defined in the Lua script. | ||
- | lua_pop(LuaState, 1); // Pop whatever is not a function. | ||
- | return false; | ||
- | } | ||
- | |||
- | va_list vl; | ||
- | |||
- | va_start(vl, Signature); | ||
- | const bool Result=StartNewCoroutine(0, Signature, vl, std::string("global function ")+FuncName+"()"); | ||
- | va_end(vl); | ||
- | |||
- | return Result; | ||
- | } | ||
- | |||
- | |||
- | bool GuiImplT::CallLuaMethod(WindowPtrT Window, const char* MethodName, const char* Signature, ...) | ||
- | { | ||
- | va_list vl; | ||
- | |||
- | va_start(vl, Signature); | ||
- | const bool Result=Window->CallLuaMethod(MethodName, Signature, vl); | ||
- | va_end(vl); | ||
- | |||
- | return Result; | ||
- | } | ||
- | |||
- | |||
- | void GuiImplT::SetEntityInfo(const char* EntityName_, void* /*EntityInstancePtr_*/) | ||
- | { | ||
- | EntityName=EntityName_; | ||
- | // EntityInstancePtr=EntityInstancePtr_; | ||
- | } | ||
- | |||
- | |||
- | void GuiImplT::RegisterScriptLib(const char* LibName, const luaL_Reg Functions[]) | ||
- | { | ||
- | luaL_register(LuaState, LibName, Functions); | ||
- | lua_pop(LuaState, 1); // Remove the LibName table from the stack (it was left there by the luaL_register() function). | ||
- | } | ||
- | |||
- | |||
- | static void CountHookFunction(lua_State* CrtState, lua_Debug* ar) | ||
- | { | ||
- | assert(ar->event==LUA_HOOKCOUNT); | ||
- | |||
- | luaL_error(CrtState, "Instruction count exceeds predefined limit (infinite loop error)."); | ||
- | } | ||
- | |||
- | |||
- | /** | ||
- | * This method calls a Lua function in the context of the LuaState. | ||
- | * | ||
- | * As a prerequisite, the caller must have pushed the Lua function to be called onto the stack of LuaState, | ||
- | * followed by any extra arguments (that is, arguments that are not passed in the Signature/vl parameters) at subsequent stack positions | ||
- | * (as for example the alter ego instance of a window that is to be used as the "self" or "this" value of an object-oriented method call). | ||
- | * At stack positions "below" the function to be called there may be other stack values that this method will not touch. | ||
- | * Additional parameters to the Lua function are appended from the vl parameter list as described by the Signature. | ||
- | * Return values of the Lua function are returned to the vl parameter list as described by the Signature. | ||
- | * | ||
- | * The function call is implemented as the resumption of a coroutine (the coroutine instance is newly created), | ||
- | * so that the script code can call coroutine.yield() and its derivatives like wait(), waitFrame(), etc. | ||
- | */ | ||
- | bool GuiImplT::StartNewCoroutine(int NumExtraArgs, const char* Signature, va_list vl, const std::string& DbgName) | ||
- | { | ||
- | // Create a new coroutine for this function call (or else they cannot call coroutine.yield()). | ||
- | // The new coroutine is pushed onto the stack of LuaState as a value of type "thread". | ||
- | lua_State* NewThread=lua_newthread(LuaState); | ||
- | |||
- | // Move the new thread from the top (-1) stack position to the position "below" the function and its extra parameters. | ||
- | lua_insert(LuaState, -(1+NumExtraArgs+1)); | ||
- | |||
- | // Move the function and its extra parameters to the stack of NewThread. | ||
- | lua_xmove(LuaState, NewThread, NumExtraArgs+1); | ||
- | |||
- | |||
- | // Put all other arguments for the function onto the stack of NewThread. | ||
- | // ********************************************************************* | ||
- | |||
- | const char* Results=""; | ||
- | |||
- | for (const char* c=Signature; *c; c++) | ||
- | { | ||
- | if (*c=='>') | ||
- | { | ||
- | Results=c+1; | ||
- | break; | ||
- | } | ||
- | |||
- | switch (*c) | ||
- | { | ||
- | // According to the g++ compiler, bool is promoted to int, and float is promoted to double when passed through '...', | ||
- | // and therefore we should pass int and double to va_arg() instead of bool and float. | ||
- | case 'b': lua_pushboolean(NewThread, va_arg(vl, /*bool*/int )); break; | ||
- | case 'i': lua_pushinteger(NewThread, va_arg(vl, int )); break; | ||
- | case 'f': lua_pushnumber (NewThread, va_arg(vl, /*float*/double)); break; | ||
- | case 'd': lua_pushnumber (NewThread, va_arg(vl, double )); break; | ||
- | case 's': lua_pushstring (NewThread, va_arg(vl, char* )); break; | ||
- | // case 'E': lua_getglobal (NewThread, va_arg(vl, BaseEntityT*)->Name.c_str()); break; | ||
- | |||
- | default: | ||
- | Console->Warning(std::string("Invalid signature \"")+Signature+"\" in call to "+DbgName+".\n"); | ||
- | lua_settop(NewThread, 0); // Clear the stack of NewThread (the function and its arguments). | ||
- | lua_pop(LuaState, 1); // Pop the thread (it will then be garbage collected). | ||
- | return false; | ||
- | } | ||
- | |||
- | // WARNING: Do NOT issue a call like lua_tostring(NewThread, -1) here! | ||
- | // This is because "If the value is a number, then lua_tolstring also changes the actual value in the stack to a string.", | ||
- | // as described at http://www.lua.org/manual/5.1/manual.html#lua_tolstring | ||
- | } | ||
- | |||
- | const int ResCount=strlen(Results); | ||
- | |||
- | |||
- | // Do the actual function call. | ||
- | // **************************** | ||
- | |||
- | // Set the hook function for the "count" event, so that we can detect and prevent infinite loops. | ||
- | lua_sethook(NewThread, CountHookFunction, LUA_MASKCOUNT, 10000); // Should have a ConVar for the number of instruction counts!? | ||
- | |||
- | // Start the new coroutine. | ||
- | const int ThreadResult=lua_resume(NewThread, lua_gettop(NewThread)-1); | ||
- | |||
- | |||
- | // Deal with the results. | ||
- | // ********************** | ||
- | |||
- | if (ThreadResult==0) | ||
- | { | ||
- | // The coroutine returned normally, now return the results to the caller. | ||
- | int StackIndex=1; | ||
- | |||
- | // If we expect more arguments back than we got, push a single nil that will help to fill-up any number of missing arguments. | ||
- | if (ResCount>lua_gettop(NewThread)) lua_pushnil(NewThread); | ||
- | |||
- | for (const char* c=Results; *c; c++) | ||
- | { | ||
- | switch (*c) | ||
- | { | ||
- | case 'b': *va_arg(vl, bool* )=lua_toboolean(NewThread, StackIndex)!=0; break; | ||
- | case 'i': *va_arg(vl, int* )=lua_tointeger(NewThread, StackIndex); break; | ||
- | case 'f': *va_arg(vl, float* )=float(lua_tonumber(NewThread, StackIndex)); break; | ||
- | case 'd': *va_arg(vl, double*)=lua_tonumber(NewThread, StackIndex); break; | ||
- | // case 'E': *va_arg(vl, BaseEntityT**)=(BaseEntityT*)ScriptStateT::GetCheckedObjectParam(NewThread, StackIndex, BaseEntityT::TypeInfo); break; | ||
- | |||
- | case 's': | ||
- | { | ||
- | const char* s=lua_tostring(NewThread, StackIndex); | ||
- | static const char* e=""; | ||
- | |||
- | *va_arg(vl, const char**)=(s!=NULL) ? s : e; | ||
- | break; | ||
- | } | ||
- | |||
- | case 'S': | ||
- | { | ||
- | const char* s=lua_tostring(NewThread, StackIndex); | ||
- | |||
- | *va_arg(vl, std::string*)=(s!=NULL) ? s : ""; | ||
- | break; | ||
- | } | ||
- | |||
- | default: | ||
- | Console->Warning(std::string("Invalid results signature \"")+Signature+"\" in call to "+DbgName+".\n"); | ||
- | break; | ||
- | } | ||
- | |||
- | if (StackIndex<lua_gettop(NewThread)) StackIndex++; | ||
- | } | ||
- | |||
- | lua_settop(NewThread, 0); // Pop everything (the results) from the NewThread stack. | ||
- | lua_pop(LuaState, 1); // Pop the thread (it will then be garbage collected). | ||
- | return true; | ||
- | } | ||
- | |||
- | if (ThreadResult==LUA_YIELD) | ||
- | { | ||
- | // The argument to the coroutine.yield() call is the wait time in seconds until the coroutine is supposed to be resumed. | ||
- | // If the argument is not given (or not a number), a wait time of 0 is assumed. | ||
- | // In any case, the earliest when the coroutine will be resumed is in the next (subsequent) server Think() frame. | ||
- | |||
- | // Check the argument(s). | ||
- | if (lua_gettop(NewThread)==0) lua_pushnumber(NewThread, 0); | ||
- | if (!lua_isnumber(NewThread, -1)) lua_pushnumber(NewThread, 0); | ||
- | |||
- | CoroutineMan.PendingCoroutines.PushBack(Crt); | ||
- | |||
- | // REGISTRY["__pending_coroutines_cf"][Crt.ID]=Crt.State; | ||
- | lua_getfield(LuaState, LUA_REGISTRYINDEX, "__pending_coroutines_cf"); // Put REGISTRY["__pending_coroutines_cf"] onto the stack (index -1). | ||
- | assert(lua_istable(LuaState, -1)); // Make sure that REGISTRY["__pending_coroutines_cf"] really is a table. | ||
- | lua_pushvalue(LuaState, -2); // Duplicate the "thread" (==Ctr.State) value from index -2 to the top of the stack. | ||
- | lua_rawseti(LuaState, -2, Crt.ID); // table[Crt.ID]=Crt.State; -- Pops the value from the stack. | ||
- | lua_pop(LuaState, 1); // Pop the table again. | ||
- | |||
- | if (ResCount>0) | ||
- | Console->Warning("The call to "+DbgName+" yielded (expected return values matching signature \""+Signature+"\" instead).\n"); | ||
- | |||
- | lua_settop(NewThread, 0); // Pop everything (the parameters to coroutine.yield()) from the NewThread stack. | ||
- | lua_pop(LuaState, 1); // Pop the thread (it's kept inside the __pending_coroutines_cf table now). | ||
- | return ResCount==0; | ||
- | } | ||
- | |||
- | // ThreadResult is not 0 and not LUA_YIELD, so an error occurred when running the coroutine. | ||
- | // Note that we need an extra newline here, because (all?) the Lua error messages don't have one. | ||
- | Console->Warning("Lua error in call to "+DbgName+":\n"); | ||
- | Console->Print(std::string(lua_tostring(NewThread, -1))+"\n"); | ||
- | |||
- | // Note that the stack of NewThread was not unwound after the error (but we currently have no use for it). | ||
- | lua_settop(NewThread, 0); // Just pop everything from the NewThread stack. | ||
- | lua_pop(LuaState, 1); // Pop the thread (it will then be garbage collected). | ||
- | return false; | ||
- | } | ||
- | |||
- | |||
- | /**********************************************/ | ||
- | /*** Impementation of Lua binding functions ***/ | ||
- | /**********************************************/ | ||
- | |||
- | static GuiImplT* CheckParams(lua_State* LuaState) | ||
- | { | ||
- | luaL_argcheck(LuaState, lua_istable(LuaState, 1), 1, "Expected a table that represents a GUI."); | ||
- | lua_getfield(LuaState, 1, "__userdata_cf"); | ||
- | |||
- | GuiImplT** UserData=(GuiImplT**)luaL_checkudata(LuaState, -1, "cf::GuiSys::GuiT"); if (UserData==NULL) luaL_error(LuaState, "NULL userdata in GUI table."); | ||
- | GuiImplT* Gui =(*UserData); | ||
- | |||
- | // Pop the userdata from the stack again. Not necessary though as it doesn't hurt there. | ||
- | // lua_pop(LuaState, 1); | ||
- | return Gui; | ||
- | } | ||
- | |||
- | |||
- | int GuiImplT::Activate(lua_State* LuaState) | ||
- | { | ||
- | GuiImplT* Gui=CheckParams(LuaState); | ||
- | |||
- | // I also want to treat the number 0 as false, not just "false" and "nil". | ||
- | if (lua_isnumber(LuaState, 2)) Gui->IsActive=lua_tointeger(LuaState, 2)!=0; | ||
- | else Gui->IsActive=lua_toboolean(LuaState, 2)!=0; | ||
- | |||
- | return 0; | ||
- | } | ||
- | |||
- | |||
- | int GuiImplT::Close(lua_State* LuaState) | ||
- | { | ||
- | GuiImplT* Gui=CheckParams(LuaState); | ||
- | |||
- | Gui->IsActive=false; | ||
- | return 0; | ||
- | } | ||
- | |||
- | |||
- | int GuiImplT::SetInteractive(lua_State* LuaState) | ||
- | { | ||
- | GuiImplT* Gui=CheckParams(LuaState); | ||
- | |||
- | // I also want to treat the number 0 as false, not just "false" and "nil". | ||
- | if (lua_isnumber(LuaState, 2)) Gui->IsInteractive=lua_tointeger(LuaState, 2)!=0; | ||
- | else Gui->IsInteractive=lua_toboolean(LuaState, 2)!=0; | ||
- | |||
- | return 0; | ||
- | } | ||
- | |||
- | |||
- | int GuiImplT::SetFullCover(lua_State* LuaState) | ||
- | { | ||
- | GuiImplT* Gui=CheckParams(LuaState); | ||
- | |||
- | // I also want to treat the number 0 as false, not just "false" and "nil". | ||
- | if (lua_isnumber(LuaState, 2)) Gui->IsFullCover=lua_tointeger(LuaState, 2)!=0; | ||
- | else Gui->IsFullCover=lua_toboolean(LuaState, 2)!=0; | ||
- | |||
- | return 0; | ||
- | } | ||
- | |||
- | |||
- | int GuiImplT::SetMousePos(lua_State* LuaState) | ||
- | { | ||
- | GuiImplT* Gui=CheckParams(LuaState); | ||
- | |||
- | Gui->SetMousePos(float(lua_tonumber(LuaState, 2)), | ||
- | float(lua_tonumber(LuaState, 3))); | ||
- | |||
- | return 0; | ||
- | } | ||
- | |||
- | |||
- | int GuiImplT::SetMouseIsShown(lua_State* LuaState) | ||
- | { | ||
- | GuiImplT* Gui=CheckParams(LuaState); | ||
- | |||
- | // I also want to treat the number 0 as false, not just "false" and "nil". | ||
- | if (lua_isnumber(LuaState, 2)) Gui->MouseIsShown=lua_tointeger(LuaState, 2)!=0; | ||
- | else Gui->MouseIsShown=lua_toboolean(LuaState, 2)!=0; | ||
- | |||
- | return 0; | ||
- | } | ||
- | |||
- | |||
- | int GuiImplT::SetFocus(lua_State* LuaState) | ||
- | { | ||
- | GuiImplT* Gui=CheckParams(LuaState); | ||
- | |||
- | if (lua_isstring(LuaState, 2)) | ||
- | { | ||
- | Gui->FocusWindow=Gui->RootWindow->Find(lua_tostring(LuaState, 2)); | ||
- | } | ||
- | else if (lua_istable(LuaState, 2)) | ||
- | { | ||
- | WindowT* Win=(WindowT*)cf::GuiSys::GuiImplT::GetCheckedObjectParam(LuaState, 2, WindowT::TypeInfo); | ||
- | |||
- | Gui->FocusWindow=Win; | ||
- | } | ||
- | |||
- | // Note that we intentionally did *not* call the Lua OnFocusLose() or OnFocusGain() scripts, | ||
- | // as is done when the focus changes due to a mouse click: | ||
- | // the Lua code that calls this method should instead call the desired OnFocus*() script functions itself. | ||
- | // This behaviour must be explicitly stated in the user documentation! | ||
- | return 0; | ||
- | } | ||
- | |||
- | |||
- | int GuiImplT::GetEntityName(lua_State* LuaState) | ||
- | { | ||
- | GuiImplT* Gui=CheckParams(LuaState); | ||
- | |||
- | lua_pushstring(LuaState, Gui->EntityName.c_str()); | ||
- | return 1; | ||
- | } | ||
- | |||
- | |||
- | int GuiImplT::SetRootWindow(lua_State* LuaState) | ||
- | { | ||
- | GuiImplT* Gui=CheckParams(LuaState); | ||
- | WindowT* Win=(WindowT*)cf::GuiSys::GuiImplT::GetCheckedObjectParam(LuaState, 2, WindowT::TypeInfo); | ||
- | |||
- | // Note that Gui->RootWindow is a WindowPtrT that makes sure that the Win instance gets | ||
- | // properly anchored in the Lua state in order to prevent premature garbage collection. | ||
- | Gui->RootWindow=Win; | ||
- | return 0; | ||
- | } | ||
- | |||
- | |||
- | void GuiImplT::RegisterLua(lua_State* LuaState) | ||
- | { | ||
- | // Create a new table T and add it into the registry table with "cf::GuiSys::GuiT" as the key and T as the value. | ||
- | // This also leaves T on top of the stack. See PiL2 chapter 28.2 for more details. | ||
- | luaL_newmetatable(LuaState, "cf::GuiSys::GuiT"); | ||
- | |||
- | // See PiL2 chapter 28.3 for a great explanation on what is going on here. | ||
- | // Essentially, we set T.__index = T (the luaL_newmetatable() function left T on the top of the stack). | ||
- | lua_pushvalue(LuaState, -1); // Pushes/duplicates the new table T on the stack. | ||
- | lua_setfield(LuaState, -2, "__index"); // T.__index = T; | ||
- | |||
- | static const luaL_reg GuiMethods[]= | ||
- | { | ||
- | { "activate", Activate }, | ||
- | { "close", Close }, | ||
- | { "setInteractive", SetInteractive }, | ||
- | { "setFullCover", SetFullCover }, | ||
- | { "setMousePos", SetMousePos }, | ||
- | { "setMouseMat", SetMouseMat }, | ||
- | { "showMouse", SetMouseIsShown }, | ||
- | { "setFocus", SetFocus }, | ||
- | { "hasValidEntity", HasValidEntity }, | ||
- | { "getEntityName", GetEntityName }, | ||
- | { "SetRootWindow", SetRootWindow }, | ||
- | { "new", CreateNewWindow }, | ||
- | { "FindWindow", FindWindow }, | ||
- | { "thread", RegisterThread }, | ||
- | { "__tostring", toString }, | ||
- | { NULL, NULL } | ||
- | }; | ||
- | |||
- | // Now insert the functions listed in GuiMethods into T (the table on top of the stack). | ||
- | luaL_register(LuaState, NULL, GuiMethods); | ||
- | |||
- | // Clear the stack. | ||
- | lua_settop(LuaState, 0); | ||
- | } | ||
- | </code> | ||
- | |||
- | |||
- | ===== Libs/MaterialSystem/RendererCgARB1/Shaders/A_Diff_Light_Norm.cpp ===== | ||
- | |||
- | This file is part of the Cafu Renderer DLL that uses NVidias "Cg" GPU programming language for vertex and pixel shaders. In this case, meshes with diffuse-maps, light-maps, normal-maps and optionally specular-maps are rendered. | ||
- | |||
- | <code cpp> | ||
- | #include <GL/gl.h> | ||
- | #include <Cg/cg.h> | ||
- | #include <Cg/cgGL.h> | ||
- | |||
- | #include "../../Common/OpenGLState.hpp" | ||
- | #include "../../Mesh.hpp" | ||
- | #include "../RendererImpl.hpp" | ||
- | #include "../RenderMaterial.hpp" | ||
- | #include "../Shader.hpp" | ||
- | #include "../TextureMapImpl.hpp" | ||
- | |||
- | |||
- | using namespace MatSys; | ||
- | |||
- | |||
- | class Shader_A_Diff_Light_Norm : public ShaderT | ||
- | { | ||
- | private: | ||
- | |||
- | CGprogram VertexShader; | ||
- | CGparameter VS_EyePos; | ||
- | CGprogram FragmentShader; | ||
- | |||
- | unsigned long InitCounter; | ||
- | |||
- | |||
- | void Initialize() | ||
- | { | ||
- | VertexShader=UploadCgProgram(RendererImplT::GetInstance().GetCgContext(), CG_PROFILE_ARBVP1, | ||
- | " void main(in float4 InPos : POSITION, \n" | ||
- | " in float3 InNormal : NORMAL, \n" | ||
- | " in float3 InTangent : TANGENT, \n" | ||
- | " in float3 InBiNormal : BINORMAL, \n" | ||
- | " in float4 InColor : COLOR, \n" | ||
- | " in float2 InTexCoord_Diff : TEXCOORD0, \n" | ||
- | " in float2 InTexCoord_LiMa : TEXCOORD1, \n" | ||
- | " out float4 OutPos : POSITION, \n" | ||
- | " out float4 OutColor : COLOR, \n" | ||
- | " out float2 OutTexCoord_Diff : TEXCOORD0, \n" | ||
- | " out float2 OutTexCoord_LiMa : TEXCOORD1, \n" | ||
- | " out float3 OutEyeDir : TEXCOORD2, \n" | ||
- | " uniform float3 EyePos) \n" | ||
- | " { \n" | ||
- | " const float3x3 RotMat=float3x3(InTangent, \n" | ||
- | " InBiNormal, \n" | ||
- | " InNormal); \n" | ||
- | " \n" | ||
- | " OutPos =mul(glstate.matrix.mvp, InPos); \n" | ||
- | " OutColor =InColor; \n" | ||
- | " OutTexCoord_Diff=InTexCoord_Diff; \n" | ||
- | " OutTexCoord_LiMa=InTexCoord_LiMa; \n" | ||
- | " OutEyeDir =mul(RotMat, EyePos-InPos.xyz); \n" | ||
- | " } \n"); | ||
- | |||
- | VS_EyePos=cgGetNamedParameter(VertexShader, "EyePos"); | ||
- | |||
- | |||
- | FragmentShader=UploadCgProgram(RendererImplT::GetInstance().GetCgContext(), CG_PROFILE_ARBFP1, | ||
- | " void main(in half4 InColor : COLOR, \n" | ||
- | " in half2 InTexCoord_Diff : TEXCOORD0, \n" | ||
- | " in half2 InTexCoord_LiMa : TEXCOORD1, \n" | ||
- | " in half3 EyeDir : TEXCOORD2, \n" | ||
- | " out half4 OutColor : COLOR, \n" | ||
- | " uniform sampler2D DiffuseMapSampler : TEXUNIT0, \n" | ||
- | " uniform sampler2D LightMapSampler : TEXUNIT1, \n" | ||
- | " uniform sampler2D LightDirMapSampler : TEXUNIT2, \n" | ||
- | " uniform sampler2D NormalMapSampler : TEXUNIT3, \n" | ||
- | " uniform sampler2D SpecMapSampler : TEXUNIT4) \n" | ||
- | " { \n" | ||
- | " const half4 DiffuseC = tex2D(DiffuseMapSampler, InTexCoord_Diff); \n" | ||
- | " const half4 SpecularC = tex2D(SpecMapSampler, InTexCoord_Diff); \n" | ||
- | " const half4 LightMapC = tex2D(LightMapSampler, InTexCoord_LiMa); \n" | ||
- | " const half4 LightMapDirC= tex2D(LightDirMapSampler, InTexCoord_LiMa); \n" | ||
- | " const half3 LightMapDir =2.0*(LightMapDirC.xyz-0.5); \n" | ||
- | " const half3 Normal =2.0*(tex2D(NormalMapSampler, InTexCoord_Diff).xyz-0.5); \n" | ||
- | " \n" | ||
- | " const half3 HalfwayDir =normalize(normalize(EyeDir)+LightMapDir); \n" | ||
- | " \n" | ||
- | " const half4 diff =half4(saturate(dot(LightMapDir, Normal)).xxx, 1.0); \n" | ||
- | " const half4 spec =half4(pow(saturate(dot(HalfwayDir, Normal)), 32.0).xxx, 0.0); \n" | ||
- | " \n" | ||
- | " const half4 LightMapCorrectedC=half4(LightMapC.xyz/LightMapDirC.w, 1.0); \n" | ||
- | " \n" | ||
- | " OutColor=LightMapCorrectedC*InColor*(diff*DiffuseC + spec*SpecularC); \n" | ||
- | " } \n"); | ||
- | } | ||
- | |||
- | |||
- | public: | ||
- | |||
- | Shader_A_Diff_Light_Norm() | ||
- | { | ||
- | VertexShader =NULL; | ||
- | VS_EyePos =NULL; | ||
- | FragmentShader=NULL; | ||
- | |||
- | InitCounter=0; | ||
- | } | ||
- | |||
- | const std::string& GetName() const | ||
- | { | ||
- | static const std::string Name="A_Diff_Light_Norm"; | ||
- | |||
- | return Name; | ||
- | } | ||
- | |||
- | char CanHandleAmbient(const MaterialT& Material) const | ||
- | { | ||
- | if (Material.NoDraw) return 0; | ||
- | |||
- | if ( Material.DiffMapComp .IsEmpty()) return 0; | ||
- | if ( Material.LightMapComp.IsEmpty()) return 0; | ||
- | if ( Material.NormMapComp .IsEmpty()) return 0; | ||
- | if (!Material.LumaMapComp .IsEmpty()) return 0; | ||
- | |||
- | return 255; | ||
- | } | ||
- | |||
- | char CanHandleLighting(const MaterialT& /*Material*/) const | ||
- | { | ||
- | return 0; | ||
- | } | ||
- | |||
- | bool CanHandleStencilShadowVolumes() const | ||
- | { | ||
- | return false; | ||
- | } | ||
- | |||
- | void Activate() | ||
- | { | ||
- | if (InitCounter<RendererImplT::GetInstance().GetInitCounter()) | ||
- | { | ||
- | Initialize(); | ||
- | InitCounter=RendererImplT::GetInstance().GetInitCounter(); | ||
- | } | ||
- | |||
- | // These are very expensive calls! | ||
- | cgGLBindProgram(VertexShader); | ||
- | cgGLBindProgram(FragmentShader); | ||
- | } | ||
- | |||
- | void Deactivate() | ||
- | { | ||
- | } | ||
- | |||
- | bool NeedsNormals() const | ||
- | { | ||
- | return false; | ||
- | } | ||
- | |||
- | bool NeedsTangentSpace() const | ||
- | { | ||
- | return false; | ||
- | } | ||
- | |||
- | bool NeedsXYAttrib() const | ||
- | { | ||
- | return false; | ||
- | } | ||
- | |||
- | void RenderMesh(const MeshT& Mesh) | ||
- | { | ||
- | const RendererImplT& Renderer =RendererImplT::GetInstance(); | ||
- | RenderMaterialT* RM =Renderer.GetCurrentRenderMaterial(); | ||
- | const MaterialT& Material =*(RM->Material); | ||
- | const ExpressionT::SymbolsT& Sym =Renderer.GetExpressionSymbols(); | ||
- | OpenGLStateT* OpenGLState=OpenGLStateT::GetInstance(); | ||
- | const float* EyePos =Renderer.GetCurrentEyePosition(); | ||
- | |||
- | const float AlphaTestValue=Material.AlphaTestValue.Evaluate(Sym).GetAsFloat(); | ||
- | const float RedValue =Material.RedGen .Evaluate(Sym).GetAsFloat(); | ||
- | const float GreenValue =Material.GreenGen .Evaluate(Sym).GetAsFloat(); | ||
- | const float BlueValue =Material.BlueGen .Evaluate(Sym).GetAsFloat(); | ||
- | const float AlphaValue =Material.AlphaGen .Evaluate(Sym).GetAsFloat(); | ||
- | |||
- | if (InitCounter<Renderer.GetInitCounter()) | ||
- | { | ||
- | Initialize(); | ||
- | InitCounter=Renderer.GetInitCounter(); | ||
- | } | ||
- | |||
- | |||
- | // Render the diffuse map. | ||
- | if (AlphaTestValue>=0.0) | ||
- | { | ||
- | OpenGLState->Enable(GL_ALPHA_TEST); | ||
- | OpenGLState->AlphaFunc(GL_GREATER, AlphaTestValue); | ||
- | } | ||
- | else OpenGLState->Disable(GL_ALPHA_TEST); | ||
- | |||
- | if (Material.BlendFactorSrc!=MaterialT::None /*&& Material.BlendFactorDst!=MaterialT::None*/) | ||
- | { | ||
- | OpenGLState->Enable(GL_BLEND); | ||
- | OpenGLState->BlendFunc(OpenGLStateT::BlendFactorToOpenGL[Material.BlendFactorSrc], OpenGLStateT::BlendFactorToOpenGL[Material.BlendFactorDst]); | ||
- | } | ||
- | else OpenGLState->Disable(GL_BLEND); | ||
- | |||
- | if (!Material.TwoSided) | ||
- | { | ||
- | OpenGLState->Enable(GL_CULL_FACE); | ||
- | OpenGLState->FrontFace(OpenGLStateT::WindingToOpenGL[Mesh.Winding]); | ||
- | OpenGLState->CullFace(GL_BACK); | ||
- | } | ||
- | else OpenGLState->Disable(GL_CULL_FACE); | ||
- | |||
- | if (Material.DepthOffset!=0.0f) | ||
- | { | ||
- | OpenGLState->Enable(OpenGLStateT::PolygonModeToOpenGL_Offset[Material.PolygonMode]); | ||
- | OpenGLState->PolygonOffset(Material.DepthOffset, Material.DepthOffset); | ||
- | } | ||
- | else OpenGLState->Disable(OpenGLStateT::PolygonModeToOpenGL_Offset[Material.PolygonMode]); | ||
- | |||
- | OpenGLState->PolygonMode(OpenGLStateT::PolygonModeToOpenGL[Material.PolygonMode]); | ||
- | OpenGLState->DepthFunc(GL_LEQUAL); | ||
- | OpenGLState->ColorMask(Material.AmbientMask[0], Material.AmbientMask[1], Material.AmbientMask[2], Material.AmbientMask[3]); | ||
- | OpenGLState->DepthMask(Material.AmbientMask[4]); | ||
- | OpenGLState->Disable(GL_STENCIL_TEST); | ||
- | if (cf::GL_EXT_stencil_two_side_AVAIL) | ||
- | { | ||
- | OpenGLState->Disable(GL_STENCIL_TEST_TWO_SIDE_EXT); | ||
- | OpenGLState->ActiveStencilFace(GL_FRONT); | ||
- | } | ||
- | |||
- | OpenGLState->ActiveTextureUnit(GL_TEXTURE0_ARB); | ||
- | OpenGLState->Enable(GL_TEXTURE_2D); | ||
- | OpenGLState->BindTexture(GL_TEXTURE_2D, RM->DiffTexMap->GetOpenGLObject()); | ||
- | |||
- | OpenGLState->ActiveTextureUnit(GL_TEXTURE1_ARB); | ||
- | OpenGLState->Enable(GL_TEXTURE_2D); | ||
- | OpenGLState->BindTexture(GL_TEXTURE_2D, RM->UseDefaultLightMap && Renderer.GetCurrentLightMap()!=NULL ? Renderer.GetCurrentLightMap()->GetOpenGLObject() : RM->LightTexMap->GetOpenGLObject()); | ||
- | |||
- | OpenGLState->ActiveTextureUnit(GL_TEXTURE2_ARB); | ||
- | OpenGLState->Enable(GL_TEXTURE_2D); | ||
- | OpenGLState->BindTexture(GL_TEXTURE_2D, RM->UseDefaultLightMap && Renderer.GetCurrentLightDirMap()!=NULL ? Renderer.GetCurrentLightDirMap()->GetOpenGLObject() : Renderer.GetHelperLightBlue001Map()->GetOpenGLObject()); | ||
- | |||
- | OpenGLState->ActiveTextureUnit(GL_TEXTURE3_ARB); | ||
- | OpenGLState->Enable(GL_TEXTURE_2D); | ||
- | OpenGLState->BindTexture(GL_TEXTURE_2D, RM->NormTexMap->GetOpenGLObject()); | ||
- | |||
- | OpenGLState->ActiveTextureUnit(GL_TEXTURE4_ARB); | ||
- | OpenGLState->Enable(GL_TEXTURE_2D); | ||
- | OpenGLState->BindTexture(GL_TEXTURE_2D, Material.SpecMapComp.IsEmpty() ? Renderer.GetHelperBlackMap()->GetOpenGLObject() : RM->SpecTexMap->GetOpenGLObject()); | ||
- | |||
- | |||
- | // The cgGLSetParameter*() functions are very slow, so cache their parameters in order to call them as little as possible. | ||
- | static float EyePosCache[3]={ 0.0, 0.0, 0.0 }; | ||
- | if (EyePos[0]!=EyePosCache[0] || EyePos[1]!=EyePosCache[1] || EyePos[2]!=EyePosCache[2]) | ||
- | { | ||
- | cgGLSetParameter3fv(VS_EyePos, EyePos); | ||
- | EyePosCache[0]=EyePos[0]; | ||
- | EyePosCache[1]=EyePos[1]; | ||
- | EyePosCache[2]=EyePos[2]; | ||
- | } | ||
- | |||
- | |||
- | glColor4f(RedValue, GreenValue, BlueValue, AlphaValue); | ||
- | glBegin(OpenGLStateT::MeshToOpenGLType[Mesh.Type]); | ||
- | for (unsigned long VertexNr=0; VertexNr<Mesh.Vertices.Size(); VertexNr++) | ||
- | { | ||
- | cf::glMultiTexCoord2fvARB(GL_TEXTURE0_ARB, Mesh.Vertices[VertexNr].TextureCoord); | ||
- | cf::glMultiTexCoord2fvARB(GL_TEXTURE1_ARB, Mesh.Vertices[VertexNr].LightMapCoord); | ||
- | |||
- | glNormal3fv(Mesh.Vertices[VertexNr].Normal); // Normal | ||
- | cf::glMultiTexCoord3fvARB(GL_TEXTURE6_ARB, Mesh.Vertices[VertexNr].Tangent); // Tangent | ||
- | cf::glMultiTexCoord3fvARB(GL_TEXTURE7_ARB, Mesh.Vertices[VertexNr].BiNormal); // BiNormal | ||
- | |||
- | glVertex3dv(Mesh.Vertices[VertexNr].Origin); | ||
- | } | ||
- | glEnd(); | ||
- | } | ||
- | }; | ||
- | |||
- | |||
- | // The constructor of the base class automatically registers this shader with the ShaderRepository. | ||
- | static Shader_A_Diff_Light_Norm MyShader; | ||
- | </code> | ||
- | |||
- | |||
- | ===== Cafu/Cafu/Client/Client.cpp ===== | ||
- | |||
- | This file is part of the Cafu game client. The client is implemented with the [[wp>State_pattern|"State" code design pattern]]. The state management and some console functions for changing the client state are implemented in this file. | ||
- | |||
- | <code cpp> | ||
- | #include "Client.hpp" | ||
- | #include "ClientStateConnecting.hpp" | ||
- | #include "ClientStateIdle.hpp" | ||
- | #include "ClientStateInGame.hpp" | ||
- | |||
- | #include "ConsoleCommands/Console.hpp" | ||
- | #include "ConsoleCommands/ConFunc.hpp" | ||
- | #include "ConsoleCommands/ConVar.hpp" | ||
- | #include "GuiSys/Gui.hpp" | ||
- | #include "../../Common/h/NetConst.hpp" | ||
- | |||
- | extern "C" | ||
- | { | ||
- | #include <lua.h> | ||
- | #include <lualib.h> | ||
- | #include <lauxlib.h> | ||
- | } | ||
- | |||
- | |||
- | static const char* StateNames[]={ "idle", "connecting", "ingame" }; | ||
- | static ClientT* ClientPtr =NULL; | ||
- | |||
- | |||
- | ClientT::ClientT() | ||
- | : CurrentState(NULL), | ||
- | NextState(IDLE), | ||
- | Socket(INVALID_SOCKET), | ||
- | ServerAddress(0, 0, 0, 0, 0), | ||
- | PacketIDConnLess(0), | ||
- | MainMenuGui(NULL) | ||
- | { | ||
- | // Cannot do this directly in the initializer list above, because then the | ||
- | // ClientStateIdleT ctor would try to access the not yet initialized client ("this"). | ||
- | CurrentState=new ClientStateIdleT(*this); | ||
- | |||
- | assert(ClientPtr==NULL); | ||
- | ClientPtr=this; | ||
- | } | ||
- | |||
- | |||
- | ClientT::~ClientT() | ||
- | { | ||
- | if (Socket!=INVALID_SOCKET) closesocket(Socket); | ||
- | |||
- | delete CurrentState; | ||
- | |||
- | assert(ClientPtr==this); | ||
- | ClientPtr=NULL; | ||
- | } | ||
- | |||
- | |||
- | void ClientT::SetMainMenuGui(cf::GuiSys::GuiI* MainMenuGui_) | ||
- | { | ||
- | MainMenuGui=MainMenuGui_; | ||
- | |||
- | MainMenuGui->CallLuaFunc("OnClientStateChanged", "s", StateNames[GetStateID()]); | ||
- | } | ||
- | |||
- | |||
- | bool ClientT::ProcessInputEvent(const CaKeyboardEventT& KE) | ||
- | { | ||
- | UpdateCurrentState(); | ||
- | return CurrentState->ProcessInputEvent(KE); | ||
- | } | ||
- | |||
- | |||
- | bool ClientT::ProcessInputEvent(const CaMouseEventT& ME) | ||
- | { | ||
- | UpdateCurrentState(); | ||
- | return CurrentState->ProcessInputEvent(ME); | ||
- | } | ||
- | |||
- | |||
- | void ClientT::Render(float FrameTime) | ||
- | { | ||
- | UpdateCurrentState(); | ||
- | CurrentState->Render(FrameTime); | ||
- | } | ||
- | |||
- | |||
- | void ClientT::MainLoop(float FrameTime) | ||
- | { | ||
- | UpdateCurrentState(); | ||
- | CurrentState->MainLoop(FrameTime); | ||
- | } | ||
- | |||
- | |||
- | ClientT::StateIDT ClientT::GetStateID() const | ||
- | { | ||
- | assert(CurrentState!=NULL); | ||
- | |||
- | return (StateIDT)CurrentState->GetID(); | ||
- | } | ||
- | |||
- | |||
- | // Some notes about changing the state (CurrentState) of the client: | ||
- | // The state is usually changed from within another state by using a statement like: Client.NextState=IDLE; | ||
- | // The client will then make a state instance with ID NextState the CurrentState before the next call to any method of CurrentState. | ||
- | // The advantage of handling state changes like this is that the ctors and dtors of the states are properly run, that is, | ||
- | // the state that is left is properly destructed by its dtor and then the state that is entered is properly constructed by its ctor, | ||
- | // in this order. This is obviously good for the reliable management of (shared) resources. | ||
- | // Moreover, the callers don't need to really "know" the other state classes, i.e. no need for all those #include's everywhere. | ||
- | // Before that, I kept all the states as member objects as private members here, and had CurrentState point to one of them. | ||
- | // That was nice and simple, but required *very* ugly OnEnter() and OnLeave() methods for the resource management, | ||
- | // so that I rather changed to this solution that is slightly more complex but properly employs the ctors and dtors. | ||
- | void ClientT::UpdateCurrentState() | ||
- | { | ||
- | if (GetStateID()!=NextState) | ||
- | { | ||
- | // Delete the CurrentState first, then allocate the new state. | ||
- | // This makes sure that the dtor of the current state is run before the ctor of the next. | ||
- | delete CurrentState; | ||
- | |||
- | switch (NextState) | ||
- | { | ||
- | case IDLE: CurrentState=new ClientStateIdleT(*this); break; | ||
- | case CONNECTING: CurrentState=new ClientStateConnectingT(*this); break; | ||
- | case INGAME: CurrentState=new ClientStateInGameT(*this); break; | ||
- | } | ||
- | |||
- | MainMenuGui->CallLuaFunc("OnClientStateChanged", "s", StateNames[NextState]); | ||
- | } | ||
- | |||
- | assert(GetStateID()==NextState); | ||
- | } | ||
- | |||
- | |||
- | static ConVarT ClientRCPassword("cl_rc_password", "", ConVarT::FLAG_MAIN_EXE, "The password with which the client sends remote console commands to the server."); | ||
- | |||
- | |||
- | // IMPORTANT NOTE: | ||
- | // This function is actually UNRELATED to the game client, because it's more generic: | ||
- | // It can also be used when the client is disconnected (idle). | ||
- | int ClientT::ConFunc_rcon_Callback(lua_State* LuaState) | ||
- | { | ||
- | if (!ClientPtr) return 0; | ||
- | |||
- | // Die Antwort auf ein "connection-less" Packet beginnt mit 0xFFFFFFFF, gefolgt von der PacketID, | ||
- | // sodaร das Rรผckpacket unten nur noch individuell beendet werden muร. | ||
- | NetDataT OutData; | ||
- | |||
- | OutData.WriteLong(0xFFFFFFFF); | ||
- | OutData.WriteLong(ClientPtr->PacketIDConnLess++); | ||
- | OutData.WriteByte(CS0_RemoteConsoleCommand); | ||
- | OutData.WriteString(ClientRCPassword.GetValueString()); | ||
- | OutData.WriteString(luaL_checkstring(LuaState, 1)); | ||
- | |||
- | try | ||
- | { | ||
- | OutData.Send(ClientPtr->Socket, ClientPtr->ServerAddress); | ||
- | } | ||
- | catch (const NetDataT::WinSockAPIError& E) { Console->Print(cf::va("Send failed (WSA error %u)!\n", E.Error)); } | ||
- | catch (const NetDataT::MessageLength& E) { Console->Print(cf::va("Message too long (wanted %u, actual %u)!\n", E.Wanted, E.Actual)); } | ||
- | |||
- | return 0; | ||
- | } | ||
- | |||
- | static ConFuncT ConFunc_rcon("rcon", ClientT::ConFunc_rcon_Callback, ConFuncT::FLAG_MAIN_EXE, "Sends a command to the remote console, e.g. rcon(\"changeLevel('<MapName>')\")"); | ||
- | |||
- | |||
- | int ClientT::ConFunc_connect_Callback(lua_State* LuaState) | ||
- | { | ||
- | if (!ClientPtr) return luaL_error(LuaState, "The client instance is not available.\n"); | ||
- | |||
- | switch (ClientPtr->GetStateID()) | ||
- | { | ||
- | case IDLE: | ||
- | { | ||
- | const char* ServerName=luaL_checkstring(LuaState, 1); | ||
- | const int ServerPort=luaL_checkint(LuaState, 2); | ||
- | |||
- | try | ||
- | { | ||
- | // Must set the ServerAddress member before changing into connecting state. | ||
- | ClientPtr->ServerAddress=NetAddressT(ServerName, ServerPort); | ||
- | } | ||
- | catch (const NetAddressT::BadHostName& /*E*/) | ||
- | { | ||
- | return luaL_error(LuaState, "Unable to resolve server name \"%s\".\n", ServerName); | ||
- | } | ||
- | |||
- | // Ok, connect to the server. | ||
- | ClientPtr->NextState=CONNECTING; | ||
- | return 0; | ||
- | } | ||
- | |||
- | case CONNECTING: return luaL_error(LuaState, "The client is already connecting...\n"); | ||
- | case INGAME: return luaL_error(LuaState, "The client is already connected.\n"); | ||
- | } | ||
- | |||
- | return luaL_error(LuaState, "The client is in an unknown state.\n"); | ||
- | } | ||
- | |||
- | static ConFuncT ConFunc_connect("connect", ClientT::ConFunc_connect_Callback, ConFuncT::FLAG_MAIN_EXE, "Connects the client to the server."); | ||
- | |||
- | |||
- | int ClientT::ConFunc_disconnect_Callback(lua_State* LuaState) | ||
- | { | ||
- | if (!ClientPtr) return luaL_error(LuaState, "The client instance is not available.\n"); | ||
- | |||
- | switch (ClientPtr->GetStateID()) | ||
- | { | ||
- | case IDLE: return luaL_error(LuaState, "The client is not connected.\n"); | ||
- | case CONNECTING: ClientPtr->NextState=IDLE; return 0; // Abort connection attempt (premature timeout). | ||
- | case INGAME: ClientPtr->NextState=IDLE; return 0; // Ok, disconnect from server. | ||
- | } | ||
- | |||
- | return luaL_error(LuaState, "The client is in an unknown state.\n"); | ||
- | } | ||
- | |||
- | static ConFuncT ConFunc_disconnect("disconnect", ClientT::ConFunc_disconnect_Callback, ConFuncT::FLAG_MAIN_EXE, "Disconnects the client from the server."); | ||
- | </code> | ||
- | |||
- | |||
- | ===== Cafu/CaWE/DialogTerrainEdit.cpp ===== | ||
- | |||
- | This file is part of CaWE, the Cafu World Editor. It implements the "Edit Terrain" dialog. | ||
- | |||
- | <code cpp> | ||
- | #include "DialogTerrainEdit.hpp" | ||
- | |||
- | #include "MapDocument.hpp" | ||
- | #include "GameConfig.hpp" | ||
- | #include "ChildFrame.hpp" | ||
- | #include "ToolManager.hpp" | ||
- | #include "ToolTerrainEdit.hpp" | ||
- | #include "DialogTerrainGeneration.hpp" | ||
- | |||
- | #include "wx/filedlg.h" | ||
- | |||
- | |||
- | TerrainEditorDialogT::TerrainEditorDialogT(wxWindow* Parent, MapDocumentT* MapDoc, ToolTerrainEditorT* ParentTool) | ||
- | : TerrainEditorDialogFB(Parent), | ||
- | m_MapDoc(MapDoc), | ||
- | m_ParentTool(ParentTool), | ||
- | m_TerrainGenerationDialog(new TerrainGenerationDialogT(this)) | ||
- | { | ||
- | m_ToolBar->ToggleTool(ID_TOOL_RAISE, true); | ||
- | |||
- | // Notify the parent tool that this is its dialog. | ||
- | m_ParentTool->SetToolDialog(this); | ||
- | } | ||
- | |||
- | |||
- | void TerrainEditorDialogT::UpdateResolution(unsigned long Resolution) | ||
- | { | ||
- | int ResSelection=m_ChoiceResolution->FindString(wxString::Format("%lux%lu", Resolution, Resolution), true); | ||
- | |||
- | if (ResSelection==wxNOT_FOUND) | ||
- | ResSelection=m_ChoiceResolution->Append(wxString::Format("%lux%lu", Resolution, Resolution)); | ||
- | |||
- | m_ChoiceResolution->Select(ResSelection); | ||
- | } | ||
- | |||
- | |||
- | int TerrainEditorDialogT::GetActiveTool() const | ||
- | { | ||
- | if (m_ToolBar->GetToolState(TerrainEditorDialogT::ID_TOOL_RAISE )) return ID_TOOL_RAISE; | ||
- | if (m_ToolBar->GetToolState(TerrainEditorDialogT::ID_TOOL_LOWER )) return ID_TOOL_LOWER; | ||
- | if (m_ToolBar->GetToolState(TerrainEditorDialogT::ID_TOOL_FLATTEN)) return ID_TOOL_FLATTEN; | ||
- | if (m_ToolBar->GetToolState(TerrainEditorDialogT::ID_TOOL_FILL )) return ID_TOOL_FILL; | ||
- | if (m_ToolBar->GetToolState(TerrainEditorDialogT::ID_TOOL_ABLATE )) return ID_TOOL_ABLATE; | ||
- | if (m_ToolBar->GetToolState(TerrainEditorDialogT::ID_TOOL_BLUR )) return ID_TOOL_BLUR; | ||
- | if (m_ToolBar->GetToolState(TerrainEditorDialogT::ID_TOOL_SHARPEN)) return ID_TOOL_SHARPEN; | ||
- | if (m_ToolBar->GetToolState(TerrainEditorDialogT::ID_TOOL_NOISE )) return ID_TOOL_NOISE; | ||
- | if (m_ToolBar->GetToolState(TerrainEditorDialogT::ID_TOOL_ROAD )) return ID_TOOL_ROAD; | ||
- | |||
- | return -1; | ||
- | } | ||
- | |||
- | |||
- | bool TerrainEditorDialogT::OnMMouseUp() | ||
- | { | ||
- | wxCommandEvent Event; | ||
- | |||
- | if (m_ToolBar->GetToolState(TerrainEditorDialogT::ID_TOOL_RAISE)) | ||
- | { | ||
- | Event.SetId(ID_TOOL_LOWER); | ||
- | OnToolClicked(Event); | ||
- | return true; | ||
- | } | ||
- | if (m_ToolBar->GetToolState(TerrainEditorDialogT::ID_TOOL_LOWER)) | ||
- | { | ||
- | Event.SetId(ID_TOOL_RAISE); | ||
- | OnToolClicked(Event); | ||
- | return true; | ||
- | } | ||
- | |||
- | return false; | ||
- | } | ||
- | |||
- | |||
- | void TerrainEditorDialogT::OnToolClicked(wxCommandEvent& event) | ||
- | { | ||
- | // Reactivate "Tool effect" controls in case they were disabled. | ||
- | m_SpinCtrlToolEffect->Enable(); | ||
- | m_SliderToolEffect->Enable(); | ||
- | |||
- | m_ToolBar->ToggleTool(ID_TOOL_RAISE, false); | ||
- | m_ToolBar->ToggleTool(ID_TOOL_LOWER, false); | ||
- | m_ToolBar->ToggleTool(ID_TOOL_FLATTEN, false); | ||
- | m_ToolBar->ToggleTool(ID_TOOL_FILL, false); | ||
- | m_ToolBar->ToggleTool(ID_TOOL_ABLATE, false); | ||
- | m_ToolBar->ToggleTool(ID_TOOL_BLUR, false); | ||
- | m_ToolBar->ToggleTool(ID_TOOL_SHARPEN, false); | ||
- | m_ToolBar->ToggleTool(ID_TOOL_NOISE, false); | ||
- | m_ToolBar->ToggleTool(ID_TOOL_ROAD, false); | ||
- | |||
- | m_ToolBar->ToggleTool(event.GetId(), true); | ||
- | |||
- | // Disable "Tool effect" controls for tools that don't use it. | ||
- | if (event.GetId()==ID_TOOL_FLATTEN || event.GetId()==ID_TOOL_FILL || event.GetId()==ID_TOOL_ABLATE || event.GetId()==ID_TOOL_ROAD) | ||
- | { | ||
- | m_SpinCtrlToolEffect->Enable(false); | ||
- | m_SliderToolEffect->Enable(false); | ||
- | } | ||
- | } | ||
- | |||
- | |||
- | void TerrainEditorDialogT::OnSpinCtrlRadius(wxSpinEvent& event) | ||
- | { | ||
- | m_SliderRadius->SetValue(event.GetPosition()); | ||
- | |||
- | m_ParentTool->UpdateModifyWeights(); | ||
- | m_ParentTool->UpdateNoiseWeights (); | ||
- | } | ||
- | |||
- | |||
- | void TerrainEditorDialogT::OnSliderScrollRadius(wxScrollEvent& event) | ||
- | { | ||
- | m_SpinCtrlRadius->SetValue(event.GetPosition()); | ||
- | |||
- | m_ParentTool->UpdateModifyWeights(); | ||
- | m_ParentTool->UpdateNoiseWeights (); | ||
- | } | ||
- | |||
- | |||
- | void TerrainEditorDialogT::OnSpinCtrlHardness(wxSpinEvent& event) | ||
- | { | ||
- | m_SliderHardness->SetValue(event.GetPosition()); | ||
- | |||
- | m_ParentTool->UpdateModifyWeights(); | ||
- | } | ||
- | |||
- | |||
- | void TerrainEditorDialogT::OnSliderScrollHardness(wxScrollEvent& event) | ||
- | { | ||
- | m_SpinCtrlHardness->SetValue(event.GetPosition()); | ||
- | |||
- | m_ParentTool->UpdateModifyWeights(); | ||
- | } | ||
- | |||
- | |||
- | void TerrainEditorDialogT::OnSpinCtrlToolEffect(wxSpinEvent& event) | ||
- | { | ||
- | m_SliderToolEffect->SetValue(event.GetPosition()); | ||
- | |||
- | m_ParentTool->UpdateGaussWeights(); | ||
- | } | ||
- | |||
- | |||
- | void TerrainEditorDialogT::OnSliderScrollToolEffect(wxScrollEvent& event) | ||
- | { | ||
- | m_SpinCtrlToolEffect->SetValue(event.GetPosition()); | ||
- | |||
- | m_ParentTool->UpdateGaussWeights(); | ||
- | } | ||
- | |||
- | |||
- | void TerrainEditorDialogT::OnChoiceResolution(wxCommandEvent& event) | ||
- | { | ||
- | wxASSERT(m_ChoiceResolution->GetSelection()!=wxNOT_FOUND); | ||
- | |||
- | // First translate choice selection into resolution. | ||
- | unsigned long ChoiceResolution=(1<<(m_ChoiceResolution->GetSelection()+6))+1; | ||
- | |||
- | m_ParentTool->SetResolution(ChoiceResolution); | ||
- | } | ||
- | |||
- | |||
- | void TerrainEditorDialogT::OnButtonImport(wxCommandEvent& event) | ||
- | { | ||
- | if (!m_ParentTool->IsTerrainSelected()) | ||
- | { | ||
- | wxMessageBox("You can only import height data into a selected terrain.", "No terrain selected"); | ||
- | return; | ||
- | } | ||
- | |||
- | wxString HeightmapNameStr=wxFileSelector("Select a heightmap image", m_MapDoc->GetGameConfig()->ModDir+"/Terrains/", "", "", "All files (*.*)|*.*", wxFD_OPEN | wxFD_FILE_MUST_EXIST); | ||
- | |||
- | if (HeightmapNameStr!="") | ||
- | m_ParentTool->ImportHeightMap(HeightmapNameStr); | ||
- | } | ||
- | |||
- | |||
- | void TerrainEditorDialogT::OnButtonExport(wxCommandEvent& event) | ||
- | { | ||
- | if (!m_ParentTool->IsTerrainSelected()) | ||
- | { | ||
- | wxMessageBox("You can only export height data if a terrain is selected.", "No terrain selected"); | ||
- | return; | ||
- | } | ||
- | |||
- | wxFileDialog ExportDialog(this, | ||
- | "Export height data", | ||
- | m_MapDoc->GetGameConfig()->ModDir+"/Terrains", //defaultDir | ||
- | "", //defaultFile | ||
- | "Bitmap (*.bmp)|*.bmp" | ||
- | "|Portable Network Graphic (*.png)|*.png" | ||
- | "|JPEG (*.jpg)|*.jpg" | ||
- | "|Portable Graymap ASCII (*.pgm)|*.pgm" | ||
- | "|Portable Graymap binary (*.pgm)|*.pgm" | ||
- | "|Terragen file (*.ter)|*.ter", | ||
- | wxFD_SAVE | wxFD_OVERWRITE_PROMPT); | ||
- | |||
- | if (ExportDialog.ShowModal()!=wxID_OK) return; | ||
- | |||
- | wxString FileName=ExportDialog.GetFilename(); | ||
- | ToolTerrainEditorT::ExportFileTypeE ExportFileType=ToolTerrainEditorT::BMP; | ||
- | |||
- | // Since the user can choose the filetype to be exported either by entering a filename without extension and choosing a correct filter or by entering a | ||
- | // filename with extension (in which case the filter setting is ignored) we need to derive the correct file type here from the extension. | ||
- | if (FileName.EndsWith(".bmp")) | ||
- | { | ||
- | ExportFileType=ToolTerrainEditorT::BMP; | ||
- | } | ||
- | else if (FileName.EndsWith(".png")) | ||
- | { | ||
- | ExportFileType=ToolTerrainEditorT::PNG; | ||
- | } | ||
- | else if (FileName.EndsWith(".jpg")) | ||
- | { | ||
- | ExportFileType=ToolTerrainEditorT::JPG; | ||
- | } | ||
- | else if (FileName.EndsWith(".jpeg")) | ||
- | { | ||
- | ExportFileType=ToolTerrainEditorT::JPG; | ||
- | } | ||
- | else if (FileName.EndsWith(".pgm")) | ||
- | { | ||
- | switch (ExportDialog.GetFilterIndex()) | ||
- | { | ||
- | case 3: //ASCII | ||
- | ExportFileType=ToolTerrainEditorT::PGM_ASCII; | ||
- | break; | ||
- | case 4: //BINARY | ||
- | ExportFileType=ToolTerrainEditorT::PGM_BINARY; | ||
- | break; | ||
- | default: | ||
- | ExportFileType=ToolTerrainEditorT::PGM_ASCII; | ||
- | break; | ||
- | } | ||
- | } | ||
- | else if (FileName.EndsWith(".ter")) | ||
- | { | ||
- | ExportFileType=ToolTerrainEditorT::TER; | ||
- | } | ||
- | else | ||
- | { | ||
- | // Ignore file extension and set the file type according to filter setting. | ||
- | switch (ExportDialog.GetFilterIndex()) | ||
- | { | ||
- | case 0: | ||
- | ExportFileType=ToolTerrainEditorT::BMP; | ||
- | break; | ||
- | case 1: | ||
- | ExportFileType=ToolTerrainEditorT::PNG; | ||
- | break; | ||
- | case 2: | ||
- | ExportFileType=ToolTerrainEditorT::JPG; | ||
- | break; | ||
- | case 3: | ||
- | ExportFileType=ToolTerrainEditorT::PGM_ASCII; | ||
- | break; | ||
- | case 4: | ||
- | ExportFileType=ToolTerrainEditorT::PGM_BINARY; | ||
- | break; | ||
- | case 5: | ||
- | ExportFileType=ToolTerrainEditorT::TER; | ||
- | break; | ||
- | } | ||
- | } | ||
- | |||
- | m_ParentTool->ExportHeightMap(ExportDialog.GetPath(), ExportFileType); | ||
- | } | ||
- | |||
- | |||
- | void TerrainEditorDialogT::OnButtonGenerate(wxCommandEvent& event) | ||
- | { | ||
- | if (!m_ParentTool->IsTerrainSelected()) | ||
- | { | ||
- | wxMessageBox("You can only generate height data for a selected terrain.", "No terrain selected"); | ||
- | return; | ||
- | } | ||
- | |||
- | if (m_TerrainGenerationDialog->ShowModal(m_ParentTool->GetResolution())!=wxID_OK) return; | ||
- | |||
- | m_ParentTool->GenerateTerrain(m_TerrainGenerationDialog->m_SpinCtrlOctaves ->GetValue(), | ||
- | m_TerrainGenerationDialog->m_SpinCtrlFrequency ->GetValue(), | ||
- | m_TerrainGenerationDialog->m_SpinCtrlPersistence->GetValue(), | ||
- | m_TerrainGenerationDialog->m_SpinCtrlLacunarity ->GetValue(), | ||
- | m_TerrainGenerationDialog->m_SpinCtrlSeed ->GetValue()); | ||
- | } | ||
- | |||
- | |||
- | void TerrainEditorDialogT::OnClose(wxCloseEvent& event) | ||
- | { | ||
- | m_MapDoc->GetChildFrame()->GetToolManager().SetTool(TOOL_SELECTION); | ||
- | } | ||
- | </code> | ||