diff --git a/Bin/Data/LuaScripts/04_StaticScene.lua b/Bin/Data/LuaScripts/04_StaticScene.lua index 45bd45fa5..cf50a0085 100644 --- a/Bin/Data/LuaScripts/04_StaticScene.lua +++ b/Bin/Data/LuaScripts/04_StaticScene.lua @@ -95,7 +95,7 @@ function CreateInstructions() -- Position the text relative to the screen center instructionText.horizontalAlignment = HA_CENTER - instructionText.verticalAlignment= VA_CENTER + instructionText.verticalAlignment = VA_CENTER instructionText:SetPosition(0, ui.root.height / 4) end diff --git a/Bin/Data/LuaScripts/05_AnimatingScene.lua b/Bin/Data/LuaScripts/05_AnimatingScene.lua index ac34a53ee..fe5116b24 100644 --- a/Bin/Data/LuaScripts/05_AnimatingScene.lua +++ b/Bin/Data/LuaScripts/05_AnimatingScene.lua @@ -94,7 +94,7 @@ function CreateInstructions() -- Position the text relative to the screen center instructionText.horizontalAlignment = HA_CENTER - instructionText.verticalAlignment= VA_CENTER + instructionText.verticalAlignment = VA_CENTER instructionText:SetPosition(0, ui.root.height / 4) end diff --git a/Bin/Data/LuaScripts/20_HugeObjectCount.lua b/Bin/Data/LuaScripts/20_HugeObjectCount.lua index 63896b07e..92ef3cb17 100644 --- a/Bin/Data/LuaScripts/20_HugeObjectCount.lua +++ b/Bin/Data/LuaScripts/20_HugeObjectCount.lua @@ -3,6 +3,7 @@ -- - Creating a scene with 250 x 250 simple objects; -- - Competing with http://yosoygames.com.ar/wp/2013/07/ogre-2-0-is-up-to-3x-faster/ :); -- - Allowing examination of performance hotspots in the rendering code; +-- - Optionally speeding up rendering by grouping the objects using StaticModelGroup component; require "LuaScripts/Utilities/Sample" @@ -12,6 +13,7 @@ local boxNodes = {} local yaw = 0.0 local pitch = 0.0 local animate = false +local useGroups = false local context = GetContext() @@ -38,7 +40,12 @@ function Start() end function CreateScene() - scene_ = Scene(context) + if scene_ == nil then + scene_ = Scene(context) + else + scene_:Clear() + boxNodes = {} + end -- Create the Octree component to the scene so that drawable objects can be rendered. Use default volume -- (-1000, -1000, -1000) to (1000, 1000, 1000) @@ -54,42 +61,74 @@ function CreateScene() -- Create a directional light local lightNode = scene_:CreateChild("DirectionalLight") - lightNode.direction = Vector3(0.5, -1.0, 0.5) -- The direction vector does not need to be normalized + lightNode.direction = Vector3(-0.6, -1.0, -0.8) -- The direction vector does not need to be normalized local light = lightNode:CreateComponent("Light") light.lightType = LIGHT_DIRECTIONAL - light.color = Color(0.7, 0.35, 0.0) - -- Create box StaticModels in the scene - for y = -125, 125 do - for x = -125, 125 do - local boxNode = scene_:CreateChild("Box") - boxNode.position = Vector3(x * 0.3, 0.0, y * 0.3) - boxNode:SetScale(0.25) - local boxObject = boxNode:CreateComponent("StaticModel") - boxObject.model = cache:GetResource("Model", "Models/Box.mdl") - table.insert(boxNodes, boxNode) + + if not useGroups then + light.color = Color(0.7, 0.35, 0.0) + + -- Create individual box StaticModels in the scene + for y = -125, 125 do + for x = -125, 125 do + local boxNode = scene_:CreateChild("Box") + boxNode.position = Vector3(x * 0.3, 0.0, y * 0.3) + boxNode:SetScale(0.25) + local boxObject = boxNode:CreateComponent("StaticModel") + boxObject.model = cache:GetResource("Model", "Models/Box.mdl") + table.insert(boxNodes, boxNode) + end + end + else + light.color = Color(0.6, 0.6, 0.6); + light.specularIntensity = 1.5; + + -- Create StaticModelGroups in the scene + local lastGroup = nil + + for y = -125, 125 do + for x = -125, 125 do + -- Create new group if no group yet, or the group has already "enough" objects. The tradeoff is between culling + -- accuracy and the amount of CPU processing needed for all the objects. Note that the group's own transform + -- does not matter, and it does not render anything if instance nodes are not added to it + if lastGroup == nil or lastGroup.numInstanceNodes >= 25 * 25 then + local boxGroupNode = scene_:CreateChild("BoxGroup") + lastGroup = boxGroupNode:CreateComponent("StaticModelGroup") + lastGroup.model = cache:GetResource("Model", "Models/Box.mdl") + end + + local boxNode = scene_:CreateChild("Box"); + boxNode.position = Vector3(x * 0.3, 0.0, y * 0.3) + boxNode:SetScale(0.25) + table.insert(boxNodes, boxNode) + lastGroup:AddInstanceNode(boxNode); + end end end - -- Create the camera - cameraNode = scene_:CreateChild("Camera") - cameraNode.position = Vector3(0.0, 10.0, -100.0) - local camera = cameraNode:CreateComponent("Camera") - camera.farClip = 300.0 + -- Create the camera. Create it outside the scene so that we can clear the whole scene without affecting it + if cameraNode == nil then + cameraNode = Node(context) + cameraNode.position = Vector3(0.0, 10.0, -100.0) + local camera = cameraNode:CreateComponent("Camera") + camera.farClip = 300.0 + end end function CreateInstructions() -- Construct new Text object, set string to display and font to use local instructionText = ui.root:CreateChild("Text") instructionText:SetText("Use WASD keys and mouse to move\n".. - "Space to toggle animation") + "Space to toggle animation\n".. + "G to toggle object group optimization") instructionText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15) -- The text has multiple rows. Center them in relation to each other instructionText.textAlignment = HA_CENTER -- Position the text relative to the screen center instructionText.horizontalAlignment = HA_CENTER - instructionText.verticalAlignment= VA_CENTER + instructionText.verticalAlignment = VA_CENTER instructionText:SetPosition(0, ui.root.height / 4) end @@ -157,6 +196,12 @@ function HandleUpdate(eventType, eventData) animate = not animate end + -- Toggle grouped / ungrouped mode + if input:GetKeyPress(KEY_G) then + useGroups = not useGroups + CreateScene() + end + -- Move the camera, scale movement with time step MoveCamera(timeStep) diff --git a/Bin/Data/Scripts/04_StaticScene.as b/Bin/Data/Scripts/04_StaticScene.as index a40dec891..8cae930b6 100644 --- a/Bin/Data/Scripts/04_StaticScene.as +++ b/Bin/Data/Scripts/04_StaticScene.as @@ -92,7 +92,7 @@ void CreateInstructions() // Position the text relative to the screen center instructionText.horizontalAlignment = HA_CENTER; - instructionText.verticalAlignment= VA_CENTER; + instructionText.verticalAlignment = VA_CENTER; instructionText.SetPosition(0, ui.root.height / 4); } diff --git a/Bin/Data/Scripts/05_AnimatingScene.as b/Bin/Data/Scripts/05_AnimatingScene.as index 19ca3b849..b7b097576 100644 --- a/Bin/Data/Scripts/05_AnimatingScene.as +++ b/Bin/Data/Scripts/05_AnimatingScene.as @@ -94,7 +94,7 @@ void CreateInstructions() // Position the text relative to the screen center instructionText.horizontalAlignment = HA_CENTER; - instructionText.verticalAlignment= VA_CENTER; + instructionText.verticalAlignment = VA_CENTER; instructionText.SetPosition(0, ui.root.height / 4); } diff --git a/Bin/Data/Scripts/20_HugeObjectCount.as b/Bin/Data/Scripts/20_HugeObjectCount.as index e9eacb593..bb6e5dc58 100644 --- a/Bin/Data/Scripts/20_HugeObjectCount.as +++ b/Bin/Data/Scripts/20_HugeObjectCount.as @@ -3,6 +3,7 @@ // - Creating a scene with 250 x 250 simple objects; // - Competing with http://yosoygames.com.ar/wp/2013/07/ogre-2-0-is-up-to-3x-faster/ :) // - Allowing examination of performance hotspots in the rendering code; +// - Optionally speeding up rendering by grouping the objects using StaticModelGroup component; #include "Scripts/Utilities/Sample.as" @@ -12,6 +13,7 @@ Array boxNodes; float yaw = 0.0f; float pitch = 0.0f; bool animate = false; +bool useGroups = false; void Start() { @@ -33,7 +35,13 @@ void Start() void CreateScene() { - scene_ = Scene(); + if (scene_ is null) + scene_ = Scene(); + else + { + scene_.Clear(); + boxNodes.Clear(); + } // Create the Octree component to the scene so that drawable objects can be rendered. Use default volume // (-1000, -1000, -1000) to (1000, 1000, 1000) @@ -46,49 +54,87 @@ void CreateScene() zone.fogColor = Color(0.2f, 0.2f, 0.2f); zone.fogStart = 200.0f; zone.fogEnd = 300.0f; - + // Create a directional light Node@ lightNode = scene_.CreateChild("DirectionalLight"); - lightNode.direction = Vector3(0.5f, -1.0f, 0.5f); // The direction vector does not need to be normalized + lightNode.direction = Vector3(-0.6f, -1.0f, -0.8f); // The direction vector does not need to be normalized Light@ light = lightNode.CreateComponent("Light"); light.lightType = LIGHT_DIRECTIONAL; - light.color = Color(0.7f, 0.35f, 0.0f); - // Create box StaticModels in the scene - for (int y = -125; y < 125; ++y) + if (!useGroups) { - for (int x = -125; x < 125; ++x) + light.color = Color(0.7f, 0.35f, 0.0f); + + // Create individual box StaticModels in the scene + for (int y = -125; y < 125; ++y) { - Node@ boxNode = scene_.CreateChild("Box"); - boxNode.position = Vector3(x * 0.3f, 0.0f, y * 0.3f); - boxNode.SetScale(0.25f); - StaticModel@ boxObject = boxNode.CreateComponent("StaticModel"); - boxObject.model = cache.GetResource("Model", "Models/Box.mdl"); - boxNodes.Push(boxNode); + for (int x = -125; x < 125; ++x) + { + Node@ boxNode = scene_.CreateChild("Box"); + boxNode.position = Vector3(x * 0.3f, 0.0f, y * 0.3f); + boxNode.SetScale(0.25f); + StaticModel@ boxObject = boxNode.CreateComponent("StaticModel"); + boxObject.model = cache.GetResource("Model", "Models/Box.mdl"); + boxNodes.Push(boxNode); + } + } + } + else + { + light.color = Color(0.6f, 0.6f, 0.6f); + light.specularIntensity = 1.5f; + + // Create StaticModelGroups in the scene + StaticModelGroup@ lastGroup; + + for (int y = -125; y < 125; ++y) + { + for (int x = -125; x < 125; ++x) + { + // Create new group if no group yet, or the group has already "enough" objects. The tradeoff is between culling + // accuracy and the amount of CPU processing needed for all the objects. Note that the group's own transform + // does not matter, and it does not render anything if instance nodes are not added to it + if (lastGroup is null || lastGroup.numInstanceNodes >= 25 * 25) + { + Node@ boxGroupNode = scene_.CreateChild("BoxGroup"); + lastGroup = boxGroupNode.CreateComponent("StaticModelGroup"); + lastGroup.model = cache.GetResource("Model", "Models/Box.mdl"); + } + + Node@ boxNode = scene_.CreateChild("Box"); + boxNode.position = Vector3(x * 0.3f, 0.0f, y * 0.3f); + boxNode.SetScale(0.25f); + boxNodes.Push(boxNode); + lastGroup.AddInstanceNode(boxNode); + } } } - // Create the camera - cameraNode = scene_.CreateChild("Camera"); - cameraNode.position = Vector3(0.0f, 10.0f, -100.0f); - Camera@ camera = cameraNode.CreateComponent("Camera"); - camera.farClip = 300.0f; + // Create the camera. Create it outside the scene so that we can clear the whole scene without affecting it + if (cameraNode is null) + { + cameraNode = Node("Camera"); + cameraNode.position = Vector3(0.0f, 10.0f, -100.0f); + Camera@ camera = cameraNode.CreateComponent("Camera"); + camera.farClip = 300.0f; + } } void CreateInstructions() { // Construct new Text object, set string to display and font to use Text@ instructionText = ui.root.CreateChild("Text"); - instructionText.text = + instructionText.text = "Use WASD keys and mouse to move\n" - "Space to toggle animation"; + "Space to toggle animation\n" + "G to toggle object group optimization"; instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15); // The text has multiple rows. Center them in relation to each other instructionText.textAlignment = HA_CENTER; - + // Position the text relative to the screen center instructionText.horizontalAlignment = HA_CENTER; - instructionText.verticalAlignment= VA_CENTER; + instructionText.verticalAlignment = VA_CENTER; instructionText.SetPosition(0, ui.root.height / 4); } @@ -155,6 +201,13 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData) if (input.keyPress[KEY_SPACE]) animate = !animate; + // Toggle grouped / ungrouped mode + if (input.keyPress['G']) + { + useGroups = !useGroups; + CreateScene(); + } + // Move the camera, scale movement with time step MoveCamera(timeStep); diff --git a/Docs/LuaScriptAPI.dox b/Docs/LuaScriptAPI.dox index ec2c49854..d45635477 100644 --- a/Docs/LuaScriptAPI.dox +++ b/Docs/LuaScriptAPI.dox @@ -1982,6 +1982,18 @@ Properties:
- unsigned numGeometries (readonly) - unsigned occlusionLodLevel +StaticModelGroup : StaticModel + +Methods:
+- void AddInstanceNode(Node* node) +- void RemoveInstanceNode(Node* node); +- void RemoveAllInstanceNodes() +- unsigned GetNumInstanceNodes() const +- Node* GetInstanceNode(unsigned index) const + +Properties:
+- unsigned numInstanceNodes (readonly) + Pass : RefCounted Methods:
diff --git a/Docs/ScriptAPI.dox b/Docs/ScriptAPI.dox index e54b49d42..1227ee1f7 100644 --- a/Docs/ScriptAPI.dox +++ b/Docs/ScriptAPI.dox @@ -2436,6 +2436,66 @@ Properties:
- uint occlusionLodLevel +StaticModelGroup + +Methods:
+- void SendEvent(const String&, VariantMap& arg1 = VariantMap ( )) +- bool Load(File@, bool arg1 = false) +- bool Save(File@) const +- bool LoadXML(const XMLElement&, bool arg1 = false) +- bool SaveXML(XMLElement&) const +- void ApplyAttributes() +- bool SetAttribute(const String&, const Variant&) +- void ResetToDefault() +- void RemoveInstanceDefault() +- Variant GetAttribute(const String&) const +- Variant GetAttributeDefault(const String&) const +- void Remove() +- void MarkNetworkUpdate() const +- void DrawDebugGeometry(DebugRenderer@, bool) +- void AddInstanceNode(Node@) +- void RemoveInstanceNode(Node@) +- void RemoveAllInstanceNodes() + +Properties:
+- int refs (readonly) +- int weakRefs (readonly) +- ShortStringHash type (readonly) +- String typeName (readonly) +- String category (readonly) +- uint numAttributes (readonly) +- Variant[] attributes +- Variant[] attributeDefaults (readonly) +- AttributeInfo[] attributeInfos (readonly) +- bool temporary +- bool enabled +- bool enabledEffective (readonly) +- uint id (readonly) +- Node@ node (readonly) +- bool inView (readonly) +- bool castShadows +- bool occluder +- bool occludee +- float drawDistance +- float shadowDistance +- float lodBias +- uint viewMask +- uint lightMask +- uint shadowMask +- uint zoneMask +- uint maxLights +- BoundingBox boundingBox (readonly) +- BoundingBox worldBoundingBox (readonly) +- Model@ model +- Material@ material (writeonly) +- Material@[] materials +- uint numGeometries (readonly) +- Zone@ zone (readonly) +- uint occlusionLodLevel +- uint numInstanceNodes (readonly) +- Node@[] instanceNodes (readonly) + + Skybox Methods:
diff --git a/Source/Engine/Script/GraphicsAPI.cpp b/Source/Engine/Script/GraphicsAPI.cpp index 0a0dc1f2e..382365a43 100644 --- a/Source/Engine/Script/GraphicsAPI.cpp +++ b/Source/Engine/Script/GraphicsAPI.cpp @@ -38,6 +38,7 @@ #include "RenderPath.h" #include "Scene.h" #include "SmoothedTransform.h" +#include "StaticModelGroup.h" #include "Technique.h" #include "Terrain.h" #include "TerrainPatch.h" @@ -817,6 +818,18 @@ static void RegisterStaticModel(asIScriptEngine* engine) engine->RegisterObjectMethod("StaticModel", "uint get_occlusionLodLevel() const", asMETHOD(StaticModel, GetOcclusionLodLevel), asCALL_THISCALL); } +static void RegisterStaticModelGroup(asIScriptEngine* engine) +{ + RegisterStaticModel(engine, "StaticModelGroup", true); + engine->RegisterObjectMethod("StaticModelGroup", "void set_occlusionLodLevel(uint) const", asMETHOD(StaticModelGroup, SetOcclusionLodLevel), asCALL_THISCALL); + engine->RegisterObjectMethod("StaticModelGroup", "uint get_occlusionLodLevel() const", asMETHOD(StaticModelGroup, GetOcclusionLodLevel), asCALL_THISCALL); + engine->RegisterObjectMethod("StaticModelGroup", "void AddInstanceNode(Node@+)", asMETHOD(StaticModelGroup, AddInstanceNode), asCALL_THISCALL); + engine->RegisterObjectMethod("StaticModelGroup", "void RemoveInstanceNode(Node@+)", asMETHOD(StaticModelGroup, RemoveInstanceNode), asCALL_THISCALL); + engine->RegisterObjectMethod("StaticModelGroup", "void RemoveAllInstanceNodes()", asMETHOD(StaticModelGroup, RemoveAllInstanceNodes), asCALL_THISCALL); + engine->RegisterObjectMethod("StaticModelGroup", "uint get_numInstanceNodes() const", asMETHOD(StaticModelGroup, GetNumInstanceNodes), asCALL_THISCALL); + engine->RegisterObjectMethod("StaticModelGroup", "Node@+ get_instanceNodes(uint) const", asMETHOD(StaticModelGroup, GetInstanceNode), asCALL_THISCALL); +} + static void RegisterSkybox(asIScriptEngine* engine) { RegisterStaticModel(engine, "Skybox", true); @@ -1438,6 +1451,7 @@ void RegisterGraphicsAPI(asIScriptEngine* engine) RegisterLight(engine); RegisterZone(engine); RegisterStaticModel(engine); + RegisterStaticModelGroup(engine); RegisterSkybox(engine); RegisterAnimatedModel(engine); RegisterAnimationController(engine); diff --git a/Source/Extras/LuaScript/pkgs/Graphics/StaticModelGroup.pkg b/Source/Extras/LuaScript/pkgs/Graphics/StaticModelGroup.pkg new file mode 100644 index 000000000..188354840 --- /dev/null +++ b/Source/Extras/LuaScript/pkgs/Graphics/StaticModelGroup.pkg @@ -0,0 +1,13 @@ +$#include "StaticModelGroup.h" + +class StaticModelGroup : public StaticModel +{ + void AddInstanceNode(Node* node); + void RemoveInstanceNode(Node* node); + void RemoveAllInstanceNodes(); + + unsigned GetNumInstanceNodes() const; + Node* GetInstanceNode(unsigned index) const; + + tolua_readonly tolua_property__get_set unsigned numInstanceNodes; +}; diff --git a/Source/Extras/LuaScript/pkgs/GraphicsLuaAPI.pkg b/Source/Extras/LuaScript/pkgs/GraphicsLuaAPI.pkg index 3e862c1f6..428e456d4 100644 --- a/Source/Extras/LuaScript/pkgs/GraphicsLuaAPI.pkg +++ b/Source/Extras/LuaScript/pkgs/GraphicsLuaAPI.pkg @@ -21,6 +21,7 @@ $pfile "Graphics/RenderSurface.pkg" $pfile "Graphics/Skeleton.pkg" $pfile "Graphics/Skybox.pkg" $pfile "Graphics/StaticModel.pkg" +$pfile "Graphics/StaticModelGroup.pkg" $pfile "Graphics/Technique.pkg" $pfile "Graphics/Terrain.pkg" $pfile "Graphics/TerrainPatch.pkg" diff --git a/Source/Samples/20_HugeObjectCount/HugeObjectCount.cpp b/Source/Samples/20_HugeObjectCount/HugeObjectCount.cpp index 0abdc88bc..10b9c83a8 100644 --- a/Source/Samples/20_HugeObjectCount/HugeObjectCount.cpp +++ b/Source/Samples/20_HugeObjectCount/HugeObjectCount.cpp @@ -134,7 +134,7 @@ void HugeObjectCount::CreateScene() // Create new group if no group yet, or the group has already "enough" objects. The tradeoff is between culling // accuracy and the amount of CPU processing needed for all the objects. Note that the group's own transform // does not matter, and it does not render anything if instance nodes are not added to it - if (!lastGroup || lastGroup->GetNumInstanceNodes() >= 25* 25) + if (!lastGroup || lastGroup->GetNumInstanceNodes() >= 25 * 25) { Node* boxGroupNode = scene_->CreateChild("BoxGroup"); lastGroup = boxGroupNode->CreateComponent(); @@ -170,7 +170,7 @@ void HugeObjectCount::CreateInstructions() instructionText->SetText( "Use WASD keys and mouse to move\n" "Space to toggle animation\n" - "G to toggle object group optimization\n" + "G to toggle object group optimization" ); instructionText->SetFont(cache->GetResource("Fonts/Anonymous Pro.ttf"), 15); // The text has multiple rows. Center them in relation to each other diff --git a/Source/Samples/20_HugeObjectCount/HugeObjectCount.h b/Source/Samples/20_HugeObjectCount/HugeObjectCount.h index 040c1e6fd..aa6276150 100644 --- a/Source/Samples/20_HugeObjectCount/HugeObjectCount.h +++ b/Source/Samples/20_HugeObjectCount/HugeObjectCount.h @@ -38,6 +38,7 @@ class Scene; /// - Competing with http://yosoygames.com.ar/wp/2013/07/ogre-2-0-is-up-to-3x-faster/ :) /// - Allowing examination of performance hotspots in the rendering code; /// - Using the profiler to measure the time taken to animate the scene; +/// - Optionally speeding up rendering by grouping the objects using StaticModelGroup component; class HugeObjectCount : public Sample { OBJECT(HugeObjectCount);