34_DynamicGeometry.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. -- Dynamic geometry example.
  2. -- This sample demonstrates:
  3. -- - Cloning a Model resource
  4. -- - Modifying the vertex buffer data of the cloned models at runtime to efficiently animate them
  5. -- - Creating a Model resource and its buffer data from scratch
  6. require "LuaScripts/Utilities/Sample"
  7. local boxNodes = {}
  8. local animate = false
  9. local useGroups = false
  10. local animate = true;
  11. local animTime = 0.0
  12. local originalVertexData = VectorBuffer()
  13. local animatingBuffers = {}
  14. local originalVertices = {}
  15. local vertexDuplicates = {}
  16. function Start()
  17. -- Execute the common startup for samples
  18. SampleStart()
  19. -- Create the scene content
  20. CreateScene()
  21. -- Create the UI content
  22. CreateInstructions()
  23. -- Setup the viewport for displaying the scene
  24. SetupViewport()
  25. -- Hook up to the frame update events
  26. SubscribeToEvents()
  27. end
  28. function CreateScene()
  29. scene_ = Scene()
  30. -- Create the Octree component to the scene so that drawable objects can be rendered. Use default volume
  31. -- (-1000, -1000, -1000) to (1000, 1000, 1000)
  32. scene_:CreateComponent("Octree")
  33. -- Create a Zone for ambient light & fog control
  34. local zoneNode = scene_:CreateChild("Zone")
  35. local zone = zoneNode:CreateComponent("Zone")
  36. zone.boundingBox = BoundingBox(-1000.0, 1000.0)
  37. zone.fogColor = Color(0.2, 0.2, 0.2)
  38. zone.fogStart = 200.0
  39. zone.fogEnd = 300.0
  40. -- Create a directional light
  41. local lightNode = scene_:CreateChild("DirectionalLight")
  42. lightNode.direction = Vector3(-0.6, -1.0, -0.8) -- The direction vector does not need to be normalized
  43. local light = lightNode:CreateComponent("Light")
  44. light.lightType = LIGHT_DIRECTIONAL
  45. light.color = Color(0.4, 1.0, 0.4)
  46. light.specularIntensity = 1.5
  47. -- Get the original model and its unmodified vertices, which are used as source data for the animation
  48. local originalModel = cache:GetResource("Model", "Models/Box.mdl")
  49. if originalModel == nil then
  50. print("Model not found, cannot initialize example scene")
  51. return
  52. end
  53. -- Get the vertex buffer from the first geometry's first LOD level
  54. local buffer = originalModel:GetGeometry(0, 0):GetVertexBuffer(0)
  55. originalVertexData = buffer:GetData()
  56. local numVertices = buffer.vertexCount
  57. local vertexSize = buffer.vertexSize
  58. -- Copy the original vertex positions
  59. for i = 0, numVertices - 1 do
  60. originalVertexData:Seek(i * vertexSize)
  61. originalVertices[i+1] = originalVertexData:ReadVector3()
  62. end
  63. -- Detect duplicate vertices to allow seamless animation
  64. for i = 1, table.getn(originalVertices) do
  65. vertexDuplicates[i] = i -- Assume not a duplicate
  66. for j = 1, i - 1 do
  67. if originalVertices[i]:Equals(originalVertices[j]) then
  68. vertexDuplicates[i] = j
  69. break
  70. end
  71. end
  72. end
  73. -- Create StaticModels in the scene. Clone the model for each so that we can modify the vertex data individually
  74. for y = -1, 1 do
  75. for x = -1, 1 do
  76. local node = scene_:CreateChild("Object")
  77. node.position = Vector3(x * 2.0, 0.0, y * 2.0)
  78. local object = node:CreateComponent("StaticModel")
  79. local cloneModel = originalModel:Clone()
  80. object.model = cloneModel
  81. -- Store the cloned vertex buffer that we will modify when animating
  82. table.insert(animatingBuffers, cloneModel:GetGeometry(0, 0):GetVertexBuffer(0))
  83. end
  84. end
  85. -- Finally create one model (pyramid shape) and a StaticModel to display it from scratch
  86. -- Note: there are duplicated vertices to enable face normals. We will calculate normals programmatically
  87. local numVertices = 18
  88. local vertexData = {
  89. -- Position Normal
  90. 0.0, 0.5, 0.0, 0.0, 0.0, 0.0,
  91. 0.5, -0.5, 0.5, 0.0, 0.0, 0.0,
  92. 0.5, -0.5, -0.5, 0.0, 0.0, 0.0,
  93. 0.0, 0.5, 0.0, 0.0, 0.0, 0.0,
  94. -0.5, -0.5, 0.5, 0.0, 0.0, 0.0,
  95. 0.5, -0.5, 0.5, 0.0, 0.0, 0.0,
  96. 0.0, 0.5, 0.0, 0.0, 0.0, 0.0,
  97. -0.5, -0.5, -0.5, 0.0, 0.0, 0.0,
  98. -0.5, -0.5, 0.5, 0.0, 0.0, 0.0,
  99. 0.0, 0.5, 0.0, 0.0, 0.0, 0.0,
  100. 0.5, -0.5, -0.5, 0.0, 0.0, 0.0,
  101. -0.5, -0.5, -0.5, 0.0, 0.0, 0.0,
  102. 0.5, -0.5, -0.5, 0.0, 0.0, 0.0,
  103. 0.5, -0.5, 0.5, 0.0, 0.0, 0.0,
  104. -0.5, -0.5, 0.5, 0.0, 0.0, 0.0,
  105. 0.5, -0.5, -0.5, 0.0, 0.0, 0.0,
  106. -0.5, -0.5, 0.5, 0.0, 0.0, 0.0,
  107. -0.5, -0.5, -0.5, 0.0, 0.0, 0.0
  108. }
  109. local indexData = {
  110. 0, 1, 2,
  111. 3, 4, 5,
  112. 6, 7, 8,
  113. 9, 10, 11,
  114. 12, 13, 14,
  115. 15, 16, 17
  116. }
  117. -- Calculate face normals now
  118. for i = 0, numVertices - 1, 3 do
  119. local v1 = Vector3(vertexData[6 * i + 1], vertexData[6 * i + 2], vertexData[6 * i + 3])
  120. local v2 = Vector3(vertexData[6 * i + 7], vertexData[6 * i + 8], vertexData[6 * i + 9])
  121. local v3 = Vector3(vertexData[6 * i + 13], vertexData[6 * i + 14], vertexData[6 * i + 15])
  122. local edge1 = v1 - v2
  123. local edge2 = v1 - v3
  124. local normal = edge1:CrossProduct(edge2):Normalized()
  125. vertexData[6 * i + 4] = normal.x
  126. vertexData[6 * i + 5] = normal.y
  127. vertexData[6 * i + 6] = normal.z
  128. vertexData[6 * i + 10] = normal.x
  129. vertexData[6 * i + 11] = normal.y
  130. vertexData[6 * i + 12] = normal.z
  131. vertexData[6 * i + 16] = normal.x
  132. vertexData[6 * i + 17] = normal.y
  133. vertexData[6 * i + 18] = normal.z
  134. end
  135. -- Create model, buffers and geometry without garbage collection, as they will be managed
  136. -- by the StaticModel component once assigned to it
  137. local fromScratchModel = Model:new()
  138. local vb = VertexBuffer:new()
  139. local ib = IndexBuffer:new()
  140. local geom = Geometry:new()
  141. -- Shadowed buffer needed for raycasts to work, and so that data can be automatically restored on device loss
  142. vb.shadowed = true
  143. vb:SetSize(numVertices, MASK_POSITION + MASK_NORMAL)
  144. local temp = VectorBuffer()
  145. for i = 1, numVertices * 6 do
  146. temp:WriteFloat(vertexData[i])
  147. end
  148. vb:SetData(temp)
  149. ib.shadowed = true
  150. ib:SetSize(numVertices, false)
  151. temp:Clear()
  152. for i = 1, numVertices do
  153. temp:WriteUShort(indexData[i])
  154. end
  155. ib:SetData(temp)
  156. geom:SetVertexBuffer(0, vb)
  157. geom:SetIndexBuffer(ib)
  158. geom:SetDrawRange(TRIANGLE_LIST, 0, numVertices)
  159. fromScratchModel.numGeometries = 1
  160. fromScratchModel:SetGeometry(0, 0, geom)
  161. fromScratchModel.boundingBox = BoundingBox(Vector3(-0.5, -0.5, -0.5), Vector3(0.5, 0.5, 0.5))
  162. local node = scene_:CreateChild("FromScratchObject")
  163. node.position = Vector3(0.0, 3.0, 0.0)
  164. local object = node:CreateComponent("StaticModel")
  165. object.model = fromScratchModel
  166. -- Create the camera. Create it outside the scene so that we can clear the whole scene without affecting it
  167. cameraNode = Node()
  168. cameraNode.position = Vector3(0.0, 2.0, -20.0)
  169. local camera = cameraNode:CreateComponent("Camera")
  170. camera.farClip = 300.0
  171. end
  172. function CreateInstructions()
  173. -- Construct new Text object, set string to display and font to use
  174. local instructionText = ui.root:CreateChild("Text")
  175. instructionText:SetText("Use WASD keys and mouse/touch to move\n"..
  176. "Space to toggle animation")
  177. instructionText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15)
  178. -- The text has multiple rows. Center them in relation to each other
  179. instructionText.textAlignment = HA_CENTER
  180. -- Position the text relative to the screen center
  181. instructionText.horizontalAlignment = HA_CENTER
  182. instructionText.verticalAlignment = VA_CENTER
  183. instructionText:SetPosition(0, ui.root.height / 4)
  184. end
  185. function SetupViewport()
  186. -- Set up a viewport to the Renderer subsystem so that the 3D scene can be seen
  187. local viewport = Viewport:new(scene_, cameraNode:GetComponent("Camera"))
  188. renderer:SetViewport(0, viewport)
  189. end
  190. function SubscribeToEvents()
  191. -- Subscribe HandleUpdate() function for processing update events
  192. SubscribeToEvent("Update", "HandleUpdate")
  193. end
  194. function MoveCamera(timeStep)
  195. -- Do not move if the UI has a focused element (the console)
  196. if ui.focusElement ~= nil then
  197. return
  198. end
  199. -- Movement speed as world units per second
  200. local MOVE_SPEED = 20.0
  201. -- Mouse sensitivity as degrees per pixel
  202. local MOUSE_SENSITIVITY = 0.1
  203. -- Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch between -90 and 90 degrees
  204. local mouseMove = input.mouseMove
  205. yaw = yaw + MOUSE_SENSITIVITY * mouseMove.x
  206. pitch = pitch + MOUSE_SENSITIVITY * mouseMove.y
  207. pitch = Clamp(pitch, -90.0, 90.0)
  208. -- Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
  209. cameraNode.rotation = Quaternion(pitch, yaw, 0.0)
  210. -- Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
  211. if input:GetKeyDown(KEY_W) then
  212. cameraNode:Translate(Vector3(0.0, 0.0, 1.0) * MOVE_SPEED * timeStep)
  213. end
  214. if input:GetKeyDown(KEY_S) then
  215. cameraNode:Translate(Vector3(0.0, 0.0, -1.0) * MOVE_SPEED * timeStep)
  216. end
  217. if input:GetKeyDown(KEY_A) then
  218. cameraNode:Translate(Vector3(-1.0, 0.0, 0.0) * MOVE_SPEED * timeStep)
  219. end
  220. if input:GetKeyDown(KEY_D) then
  221. cameraNode:Translate(Vector3(1.0, 0.0, 0.0) * MOVE_SPEED * timeStep)
  222. end
  223. end
  224. function AnimateObjects(timeStep)
  225. animTime = animTime + timeStep * 100.0;
  226. -- Repeat for each of the cloned vertex buffers
  227. for i = 1, table.getn(animatingBuffers) do
  228. local startPhase = animTime + i * 30.0
  229. local buffer = animatingBuffers[i]
  230. -- Need to prepare a VectorBuffer with all data (positions, normals, uvs...)
  231. local newData = VectorBuffer()
  232. local numVertices = buffer.vertexCount
  233. local vertexSize = buffer.vertexSize
  234. for j = 1, numVertices do
  235. -- If there are duplicate vertices, animate them in phase of the original
  236. local phase = startPhase + vertexDuplicates[j] * 10.0
  237. local src = originalVertices[j]
  238. local dest = Vector3(src.x * (1.0 + 0.1 * Sin(phase)),
  239. src.y * (1.0 + 0.1 * Sin(phase + 60.0)),
  240. src.z * (1.0 + 0.1 * Sin(phase + 120.0)))
  241. -- Write position
  242. newData:WriteVector3(dest)
  243. -- Copy other vertex elements
  244. originalVertexData:Seek((j - 1) * vertexSize + 12) -- Seek past the vertex position
  245. for k = 12, vertexSize - 4, 4 do
  246. newData:WriteFloat(originalVertexData:ReadFloat())
  247. end
  248. end
  249. buffer:SetData(newData)
  250. end
  251. end
  252. function HandleUpdate(eventType, eventData)
  253. -- Take the frame time step, which is stored as a float
  254. local timeStep = eventData:GetFloat("TimeStep")
  255. -- Toggle animation with space
  256. if input:GetKeyPress(KEY_SPACE) then
  257. animate = not animate
  258. end
  259. -- Move the camera, scale movement with time step
  260. MoveCamera(timeStep)
  261. -- Animate scene if enabled
  262. if animate then
  263. AnimateObjects(timeStep)
  264. end
  265. end
  266. -- Create XML patch instructions for screen joystick layout specific to this sample app
  267. function GetScreenJoystickPatchString()
  268. return
  269. "<patch>" ..
  270. " <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
  271. " <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Animation</replace>" ..
  272. " <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
  273. " <element type=\"Text\">" ..
  274. " <attribute name=\"Name\" value=\"KeyBinding\" />" ..
  275. " <attribute name=\"Text\" value=\"SPACE\" />" ..
  276. " </element>" ..
  277. " </add>" ..
  278. "</patch>"
  279. end