Fix issue in Rocket.lua script.
[cafu-Engine.git] / Ca3DE / Ca3DEWorld.cpp
blob08fe5684f4e33f983b75a5178e883a25070d868f
1 /*
2 Cafu Engine, http://www.cafu.de/
3 Copyright (c) Carsten Fuchs and other contributors.
4 This project is licensed under the terms of the MIT license.
5 */
7 #include "Ca3DEWorld.hpp"
8 #include "EngineEntity.hpp"
9 #include "ClipSys/ClipWorld.hpp"
10 #include "ClipSys/CollisionModel_static.hpp"
11 #include "ClipSys/CollisionModelMan.hpp"
12 #include "ClipSys/TraceResult.hpp"
13 #include "ClipSys/TraceSolid.hpp"
14 #include "ConsoleCommands/Console.hpp" // For cf::va().
15 #include "GameSys/World.hpp"
16 #include "GuiSys/AllComponents.hpp" // for initing the m_ScriptState
17 #include "GuiSys/GuiImpl.hpp" // for initing the m_ScriptState
18 #include "GuiSys/Window.hpp" // for initing the m_ScriptState
19 #include "MaterialSystem/Material.hpp"
20 #include "Models/ModelManager.hpp"
21 #include "../Common/CompGameEntity.hpp"
22 #include "../Common/WorldMan.hpp"
23 #include "SceneGraph/BspTreeNode.hpp"
24 #include "String.hpp"
27 #if defined(_WIN32) && defined(_MSC_VER)
28 // Turn off warning 4355: "'this' : wird in Initialisierungslisten fuer Basisklasse verwendet".
29 #pragma warning(disable:4355)
30 #endif
33 static WorldManT WorldMan;
36 Ca3DEWorldT::Ca3DEWorldT(const char* FileName, ModelManagerT& ModelMan, cf::GuiSys::GuiResourcesT& GuiRes, bool InitForGraphics, WorldT::ProgressFunctionT ProgressFunction) /*throw (WorldT::LoadErrorT)*/
37 : m_World(WorldMan.LoadWorld(FileName, ModelMan, GuiRes, InitForGraphics, ProgressFunction)),
38 m_ClipWorld(new cf::ClipSys::ClipWorldT(m_World->m_StaticEntityData[0]->m_CollModel)),
39 m_PhysicsWorld(m_World->m_StaticEntityData[0]->m_CollModel),
40 m_ScriptState(new cf::UniScriptStateT), // Need a pointer because the dtor order is important.
41 m_ScriptWorld(NULL),
42 m_EngineEntities()
44 cf::GameSys::WorldT::InitScriptState(*m_ScriptState);
46 #if 0
47 // We cannot use this method, which in fact is kind of obsolete:
48 // It would attempt to re-register the Console and ConsoleInterface libraries,
49 // which was already done above in cf::GameSys::WorldT::InitScriptState().
50 // (Both InitScriptState() methods should probably be removed / refactored.)
51 cf::GuiSys::GuiImplT::InitScriptState(*m_ScriptState);
52 #else
54 // For each class that the TypeInfoManTs know about, add a (meta-)table to the registry of the LuaState.
55 // The (meta-)table holds the Lua methods that the respective class implements in C++,
56 // and is to be used as metatable for instances of this class.
57 cf::ScriptBinderT Binder(m_ScriptState->GetLuaState());
59 Binder.Init(cf::GuiSys::GetGuiTIM());
60 Binder.Init(cf::GuiSys::GetWindowTIM());
61 Binder.Init(cf::GuiSys::GetComponentTIM());
63 #endif
65 try
67 std::string ScriptName = cf::String::StripExt(FileName) + ".cent";
68 ScriptName = cf::String::Replace(ScriptName, "/Worlds/", "/Maps/");
69 ScriptName = cf::String::Replace(ScriptName, "\\Worlds\\", "\\Maps\\");
71 m_ScriptWorld = new cf::GameSys::WorldT(
72 InitForGraphics ? cf::GameSys::WorldT::RealmClient : cf::GameSys::WorldT::RealmServer,
73 *m_ScriptState,
74 ModelMan,
75 GuiRes,
76 *cf::ClipSys::CollModelMan, // TODO: The CollModelMan should not be a global, but rather be instantiated along with the ModelMan and GuiRes.
77 m_ClipWorld,
78 &m_PhysicsWorld);
80 m_ScriptWorld->LoadScript(ScriptName, 0 /*cf::GameSys::WorldT::InitFlag_OnlyStatic*/);
82 catch (const cf::GameSys::WorldT::InitErrorT& IE)
84 throw WorldT::LoadErrorT(IE.what());
88 ArrayT< IntrusivePtrT<cf::GameSys::EntityT> > AllEnts;
89 m_ScriptWorld->GetRootEntity()->GetAll(AllEnts);
91 // For player prototype entities make sure that their models are not shown.
92 // Showing their models may be desirable in the Map Editor, but not in the Engine.
93 for (unsigned int EntNr = 0; EntNr < AllEnts.Size(); EntNr++)
95 if (AllEnts[EntNr]->GetComponent("HumanPlayer") != NULL)
97 for (unsigned int i = 0; true; i++)
99 IntrusivePtrT<cf::GameSys::ComponentBaseT> ModelComp = AllEnts[EntNr]->GetComponent("Model", i);
101 if (ModelComp == NULL) break;
102 ModelComp->SetMember("Show", false);
107 // Create a matching
108 // - CompGameEntityT instance and
109 // - EngineEntityT instance
110 // for each entity in the script world (including the world (root) entity).
111 if (AllEnts.Size() != m_World->m_StaticEntityData.Size())
112 throw WorldT::LoadErrorT("The number of entities in the .cent file must match the number of entities in the .bsp file.");
114 for (unsigned int EntNr = 0; EntNr < AllEnts.Size(); EntNr++)
116 // This is also checked in the `cf::GameSys::WorldT` ctor, see there for details.
117 // It is repeated here as a reminder: entity IDs are used as indices into m_World->m_StaticEntityData[].
118 assert(AllEnts[EntNr]->GetID() == EntNr);
120 AllEnts[EntNr]->SetApp(new CompGameEntityT(m_World->m_StaticEntityData[EntNr]));
121 CreateNewEntityFromBasicInfo(AllEnts[EntNr], 1 /*ServerFrameNr*/);
124 // Have all components of all entities precache their resources.
125 for (unsigned int EntNr = 0; EntNr < AllEnts.Size(); EntNr++)
127 const ArrayT< IntrusivePtrT<cf::GameSys::ComponentBaseT> >& Components = AllEnts[EntNr]->GetComponents();
129 for (unsigned int CompNr = 0; CompNr < Components.Size(); CompNr++)
131 Components[CompNr]->PreCache();
137 Ca3DEWorldT::~Ca3DEWorldT()
139 // Note that the engine entities must be destructed *before* the ClipWorld,
140 // so that they properly remove their clip model from the clip world on destruction.
141 for (unsigned long EntityNr=0; EntityNr<m_EngineEntities.Size(); EntityNr++)
142 delete m_EngineEntities[EntityNr];
144 m_EngineEntities.Clear();
146 m_ScriptWorld = NULL;
148 // The script state may still hold entities that have collision model components that have registered
149 // collision models with the clip world. Thus, make sure to delete the script state before the clip world,
150 // so that the clip world is left clean.
151 delete m_ScriptState;
152 m_ScriptState = NULL;
154 // delete m_PhysicsWorld;
155 // m_PhysicsWorld = NULL;
157 delete m_ClipWorld;
158 m_ClipWorld = NULL;
160 if (m_World)
162 WorldMan.FreeWorld(m_World);
163 m_World = NULL;
168 Vector3fT Ca3DEWorldT::GetAmbientLightColorFromBB(const BoundingBox3T<double>& Dimensions, const VectorT& Origin) const
170 const Vector3dT BBCenter = scale(Dimensions.Min+Dimensions.Max, 0.5) + Origin;
171 const cf::SceneGraph::BspTreeNodeT* BspTree = m_World->m_StaticEntityData[0]->m_BspTree;
173 #if 0
174 // Performance profiling revealed that this method is frequently called
175 // and that the call to CollModel->TraceRay() is pretty expensive!
176 const Vector3dT Ray =Vector3dT(0.0, 0.0, -999999.0);
178 cf::ClipSys::TraceResultT Result(1.0);
179 CollModel->TraceRay(BBCenter, Ray, MaterialT::Clip_Radiance, Result);
181 Vector3dT Ground =BBCenter+Ray*Result.Fraction+Vector3dT(0, 0, 0.2);
182 #else
183 // We therefore revert to this call, which is similarly limited (considers faces only) but is *much* faster!
184 // Note that the proper future solution is to reimplement this method to use a precomputed lighting grid!!
185 const double Trace =BspTree->ClipLine(BBCenter, Vector3dT(0.0, 0.0, -1.0), 0.0, 999999.0);
186 Vector3dT Ground =BBCenter+Vector3dT(0.0, 0.0, -(Trace-0.2));
187 #endif
188 unsigned long LeafNr =BspTree->WhatLeaf(Ground);
190 // This is a relatively cheap (dumb) trick for dealing with problematic input (like some static detail models).
191 if (!BspTree->Leaves[LeafNr].IsInnerLeaf)
193 const VectorT TestPoints[4]={ VectorT(Dimensions.Min.x, Dimensions.Min.y, Dimensions.Max.z)+Origin,
194 VectorT(Dimensions.Min.x, Dimensions.Max.y, Dimensions.Max.z)+Origin,
195 VectorT(Dimensions.Max.x, Dimensions.Min.y, Dimensions.Max.z)+Origin,
196 VectorT(Dimensions.Max.x, Dimensions.Max.y, Dimensions.Max.z)+Origin };
198 Ground.z=-999999.0;
200 for (unsigned long TestNr=0; TestNr<4; TestNr++)
202 #if 0
203 // Same performance issue as above...
204 CollModel->TraceRay(TestPoints[TestNr], Ray, MaterialT::Clip_Radiance, Result); // BUG / FIXME: I think we have to re-init Result here!
205 const Vector3dT TestGround=TestPoints[TestNr]+Ray*Result.Fraction+Vector3dT(0, 0, 0.2);
206 #else
207 const double TestTrace =BspTree->ClipLine(TestPoints[TestNr], Vector3dT(0.0, 0.0, -1.0), 0.0, 999999.0);
208 const Vector3dT TestGround=TestPoints[TestNr]+Vector3dT(0.0, 0.0, -(TestTrace-0.2));
209 #endif
210 const unsigned long TestLeafNr=BspTree->WhatLeaf(TestGround);
212 if (!BspTree->Leaves[TestLeafNr].IsInnerLeaf) continue;
214 if (TestGround.z>Ground.z)
216 Ground=TestGround;
217 LeafNr=TestLeafNr;
222 for (unsigned long FNr=0; FNr<BspTree->Leaves[LeafNr].FaceChildrenSet.Size(); FNr++)
224 cf::SceneGraph::FaceNodeT* FaceNode=BspTree->FaceChildren[BspTree->Leaves[LeafNr].FaceChildrenSet[FNr]];
225 Vector3fT AmbientLightColor;
227 if (FaceNode->GetLightmapColorNearPosition(Ground, AmbientLightColor, BspTree->GetLightMapPatchSize()))
228 return AmbientLightColor;
231 return Vector3fT(1.0f, 1.0f, 1.0f);
235 void Ca3DEWorldT::CreateNewEntityFromBasicInfo(IntrusivePtrT<cf::GameSys::EntityT> Ent, unsigned long CreationFrameNr)
237 const unsigned long NewEntityID = Ent->GetID();
239 while (m_EngineEntities.Size() <= NewEntityID)
240 m_EngineEntities.PushBack(NULL);
242 // Well... quite clearly, EngineEntityT should be merged into the App component...!
243 // However, note that there is one very important requirement:
244 // Iterating over the m_EngineEntities array iterates the entities in the order of increasing ID.
245 // The CaServerWorldT::WriteClientDeltaUpdateMessages() and the related client code *rely* on this order!
246 assert(m_EngineEntities[NewEntityID] == NULL);
247 delete m_EngineEntities[NewEntityID];
248 m_EngineEntities[NewEntityID] = new EngineEntityT(Ent, CreationFrameNr);