34_DynamicGeometry.lua 13 KB

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