mesh.monkey2 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  1. Namespace mojo3d
  2. #rem monkeydoc The Mesh class.
  3. #end
  4. Class Mesh Extends Resource
  5. #rem monkeydoc Creates a new mesh.
  6. Creates a new empty mesh.
  7. Meshes don't actual contain instances of materials. Instead, mesh triangles are added to 'logical' materials which are effectively just integer indices.
  8. Actual materials are stored in models, and can be accessed via the [[Model.Materials]] property.
  9. #end
  10. Method New()
  11. _dirty=Null
  12. _bounds=Boxf.EmptyBounds
  13. _vertices=New Stack<Vertex3f>
  14. _materials=New Stack<MaterialID>
  15. End
  16. Method New( vertices:Vertex3f[],indices:UInt[] )
  17. Self.New()
  18. AddVertices( vertices )
  19. AddTriangles( indices )
  20. End
  21. #rem monkeydoc Number of vertices.
  22. #end
  23. Property NumVertices:Int()
  24. Return _vertices.Length
  25. End
  26. #rem monkeydoc Total number of indices.
  27. #end
  28. Property NumIndices:int()
  29. Local n:=0
  30. For Local material:=Eachin _materials
  31. n+=material.indices.Length
  32. Next
  33. Return n
  34. End
  35. #rem monkeydoc Number of materials.
  36. This will always be at least one.
  37. #end
  38. Property NumMaterials:Int()
  39. Return _materials.Length
  40. End
  41. #rem monkeydoc Mesh bounding box.
  42. #end
  43. Property Bounds:Boxf()
  44. If _dirty & Dirty.Bounds
  45. _bounds=Boxf.EmptyBounds
  46. For Local i:=0 Until _vertices.Length
  47. _bounds|=_vertices[i].position
  48. Next
  49. _dirty&=~Dirty.Bounds
  50. Endif
  51. Return _bounds
  52. End
  53. #rem monkeydoc Clears the mesh.
  54. Removes all vertices and primitives from the mesh, and resets the number of logical materials to '1'.
  55. #end
  56. Method Clear()
  57. _dirty=Null
  58. _bounds=Boxf.EmptyBounds
  59. _vertices.Clear()
  60. _materials.Clear()
  61. InvalidateVertices()
  62. End
  63. Method ClearVertices()
  64. ResizeVertices( 0 )
  65. End
  66. Method ResizeVertices( length:Int )
  67. _vertices.Resize( length )
  68. InvalidateVertices()
  69. End
  70. #rem monkeydoc Sets a range of vertices.
  71. #end
  72. Method SetVertices( vertices:Vertex3f Ptr,first:Int,count:Int )
  73. DebugAssert( first>=0 And count>=0 And first<=_vertices.Length And first+count<=_vertices.Length,"Invalid vertex range" )
  74. libc.memcpy( _vertices.Data.Data+first,vertices,count*Vertex3f.Pitch )
  75. InvalidateVertices( first,count )
  76. End
  77. Method SetVertices( vertices:Vertex3f[] )
  78. _vertices.Resize( vertices.Length )
  79. SetVertices( vertices.Data,0,vertices.Length )
  80. End
  81. #rem monkeydoc Sets a single vertex.
  82. #end
  83. Method SetVertex( index:Int,vertex:Vertex3f )
  84. DebugAssert( index>=0 And index<_vertices.Length,"Vertex index out of range" )
  85. _vertices[index]=vertex
  86. InvalidateVertices( index,1 )
  87. End
  88. #rem monkeydoc Adds vertices.
  89. #end
  90. Method AddVertices( vertices:Vertex3f Ptr,count:Int )
  91. Local first:=_vertices.Length
  92. _vertices.Resize( first+count )
  93. libc.memcpy( _vertices.Data.Data+first,vertices,count*Vertex3f.Pitch )
  94. InvalidateVertices( first,count )
  95. End
  96. Method AddVertices( vertices:Vertex3f[] )
  97. AddVertices( vertices.Data,vertices.Length )
  98. End
  99. #rem monkeydoc Adds a single vertex.
  100. #end
  101. Method AddVertex( vertex:Vertex3f )
  102. AddVertices( Varptr vertex,1 )
  103. End
  104. #rem monkeydoc Gets all vertices as an array.
  105. #end
  106. Method GetVertices:Vertex3f[]()
  107. Return _vertices.ToArray()
  108. End
  109. #rem monkeydoc Gets a single vertex.
  110. #end
  111. Method GetVertex:Vertex3f( index:Int )
  112. DebugAssert( index>=0 And index<_vertices.Length,"Vertex index out of range" )
  113. Return _vertices[index]
  114. End
  115. #rem monkeydoc Sets tthe triangles for a material in the mesh.
  116. `materialid` must be a valid material id in the range 0 to [[NumMaterials]] inclusive.
  117. #end
  118. Method SetTriangles( indices:UInt Ptr,first:Int,count:Int,materialid:Int=0 )
  119. DebugAssert( first Mod 3=0,"First must be a multiple of 3" )
  120. DebugAssert( count Mod 3=0,"Count must be a multiple of 3" )
  121. Local mindices:=GetMaterial( materialid ).indices
  122. DebugAssert( first>=0 And count>=0 And first<=mindices.Length And first+count<=mindices.Length,"Invalid range" )
  123. libc.memcpy( mindices.Data.Data+first,indices,count*IndexPitch )
  124. End
  125. Method SetTriangles( indices:UInt[],materialid:Int=0 )
  126. SetTriangles( indices.Data,indices.Length,materialid )
  127. End
  128. Method SetTriangle( index:Int,i0:Int,i1:Int,i2:Int,materialid:Int=0 )
  129. DebugAssert( index Mod 3=0,"Index must be a multiple of 3" )
  130. Local mindices:=GetMaterial( materialid ).indices
  131. DebugAssert( index>=0 And index+3<=mindices.Length,"Triangle index out of range" )
  132. mindices[index]=i0;mindices[index+1]=i1;mindices[index+2]=i2
  133. End
  134. #rem monkeydoc Adds triangles to the mesh.
  135. `count` is the number of indices to add and must be a multiple of 3.
  136. `materialid` must be a valid material id in the range 0 to [[NumMaterials]] inclusive.
  137. If `materialid` is equal to NumMaterials, a new material is automatically added first.
  138. #end
  139. Method AddTriangles( indices:UInt Ptr,count:Int,materialid:Int=0 )
  140. DebugAssert( count Mod 3=0,"Count must be a multiple of 3" )
  141. Local mindices:=GetMaterial( materialid ).indices
  142. Local first:=mindices.Length
  143. mindices.Resize( mindices.Length+count )
  144. libc.memcpy( mindices.Data.Data+first,indices,count*IndexPitch )
  145. End
  146. Method AddTriangles( indices:UInt[],materialid:Int=0 )
  147. AddTriangles( indices.Data,indices.Length,materialid )
  148. End
  149. #rem monkeydoc Adds a single triangle the mesh.
  150. #end
  151. Method AddTriangle( i0:UInt,i1:UInt,i2:UInt,materialid:Int=0 )
  152. Local indices:=GetMaterial( materialid ).indices
  153. indices.Add( i0 )
  154. indices.Add( i1 )
  155. indices.Add( i2 )
  156. End
  157. #rem monkeydoc Get indices for a material id.
  158. #end
  159. Method GetIndices:UInt[]( materialid:Int=0 )
  160. DebugAssert( materialid>=0 And materialid<_materials.Length,"Material id out of range" )
  161. Return _materials[materialid].indices.ToArray()
  162. End
  163. Method GetAllIndices:Uint[]()
  164. Local indices:=New Uint[NumIndices],ip:=indices.Data
  165. For Local material:=Eachin _materials
  166. Local mindices:=material.indices
  167. libc.memcpy( ip,mindices.Data.Data,mindices.Length*IndexPitch )
  168. ip+=mindices.Length
  169. Next
  170. Return indices
  171. End
  172. #rem monkeydoc Adds materials to the mesh.
  173. Adds `count` logical materials to the mesh.
  174. Returns the first material id of the newly added materials.
  175. #end
  176. Method AddMaterials:Int( count:Int )
  177. Local first:=_materials.Length
  178. For Local i:=0 Until count
  179. _materials.Push( New MaterialID )
  180. Next
  181. Return first
  182. End
  183. #rem monkeydoc Adds a mesh to this mesh.
  184. #end
  185. Method AddMesh( mesh:Mesh,materialid:Int=0 )
  186. Local v0:=_vertices.Length
  187. AddVertices( mesh._vertices.Data.Data,mesh._vertices.Length )
  188. For Local material:=Eachin mesh._materials
  189. Local count:=material.indices.Length
  190. Local mindices:=material.indices.Data
  191. If v0
  192. Local indices:=New UInt[count]
  193. For Local i:=0 Until count
  194. indices[i]=mindices[i]+v0
  195. Next
  196. mindices=indices
  197. Endif
  198. AddTriangles( mindices.Data,count,materialid )
  199. materialid+=1
  200. Next
  201. End
  202. #rem monkeydoc Transforms all vertices in the mesh.
  203. #end
  204. Method TransformVertices( matrix:AffineMat4f )
  205. Local vertices:=_vertices.Data
  206. Local cofactor:=matrix.m.Cofactor()
  207. For Local i:=0 Until _vertices.Length
  208. vertices[i].position=matrix * vertices[i].position
  209. vertices[i].normal=(cofactor * vertices[i].normal).Normalize()
  210. vertices[i].tangent.XYZ=(cofactor * vertices[i].tangent.XYZ).Normalize()
  211. Next
  212. InvalidateVertices()
  213. End
  214. #rem monkeydoc Fits all vertices in the mesh to a box.
  215. #end
  216. Method FitVertices( box:Boxf,uniform:Bool=True )
  217. Local bounds:=Bounds
  218. Local scale:=box.Size/bounds.Size
  219. If uniform scale=New Vec3f( Min( scale.x,Min( scale.y,scale.z ) ) )
  220. Local m:=Mat3f.Scaling( scale )
  221. Local t:=box.Center - m * bounds.Center
  222. TransformVertices( New AffineMat4f( m,t ) )
  223. End
  224. #rem monkeydoc Updates mesh normals.
  225. Recalculates all vertex normals based on triangle and vertex positions.
  226. #end
  227. Method UpdateNormals()
  228. Local vertices:=_vertices.Data
  229. For Local i:=0 Until _vertices.Length
  230. vertices[i].normal=New Vec3f(0)
  231. Next
  232. For Local material:=Eachin _materials
  233. Local indices:=material.indices.Data
  234. For Local i:=0 Until material.indices.Length Step 3
  235. Local i1:=indices[i+0]
  236. Local i2:=indices[i+1]
  237. Local i3:=indices[i+2]
  238. Local v1:=vertices[i1].position
  239. Local v2:=vertices[i2].position
  240. Local v3:=vertices[i3].position
  241. Local n:=(v2-v1).Cross(v3-v1).Normalize()
  242. vertices[i1].normal+=n
  243. vertices[i2].normal+=n
  244. vertices[i3].normal+=n
  245. Next
  246. Next
  247. For Local i:=0 Until _vertices.Length
  248. vertices[i].normal=vertices[i].normal.Normalize()
  249. Next
  250. InvalidateVertices()
  251. End
  252. #rem monkeydoc Updates mesh tangents.
  253. Recalculates all vertex tangents based on triangles, vertex normals and vertex texcoord0.
  254. #end
  255. Method UpdateTangents()
  256. Local vertices:=_vertices.Data.Data
  257. Local tan1:=New Vec3f[_vertices.Length]
  258. Local tan2:=New Vec3f[_vertices.Length]
  259. For Local material:=Eachin _materials
  260. Local indices:=material.indices.Data
  261. For Local i:=0 Until material.indices.Length Step 3
  262. Local i1:=indices[i+0]
  263. Local i2:=indices[i+1]
  264. Local i3:=indices[i+2]
  265. Local v1:=vertices+i1
  266. Local v2:=vertices+i2
  267. Local v3:=vertices+i3
  268. Local x1:=v2->Tx-v1->Tx
  269. Local x2:=v3->Tx-v1->Tx
  270. Local y1:=v2->Ty-v1->Ty
  271. Local y2:=v3->Ty-v1->Ty
  272. Local z1:=v2->Tz-v1->Tz
  273. Local z2:=v3->Tz-v1->Tz
  274. Local s1:=v2->Sx-v1->Sx
  275. Local s2:=v3->Sx-v1->Sx
  276. Local t1:=v2->Sy-v1->Sy
  277. Local t2:=v3->Sy-v1->Sy
  278. Local r:=1.0/(s1*t2-s2*t1)
  279. Local sdir:=New Vec3f( (t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r )
  280. Local tdir:=New Vec3f( (s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r )
  281. tan1[i1]+=sdir
  282. tan1[i2]+=sdir
  283. tan1[i3]+=sdir
  284. tan2[i1]+=tdir
  285. tan2[i2]+=tdir
  286. tan2[i3]+=tdir
  287. Next
  288. Next
  289. For Local i:=0 Until _vertices.Length
  290. Local v:=vertices+i
  291. Local n:=v->normal,t:=tan1[i]
  292. v->tangent.XYZ=( t - n * n.Dot( t ) ).Normalize()
  293. v->tangent.w=n.Cross( t ).Dot( tan2[i] ) < 0 ? -1 Else 1
  294. Next
  295. InvalidateVertices()
  296. End
  297. #rem monkeydoc Flips all triangles.
  298. #end
  299. Method FlipTriangles()
  300. For Local material:=Eachin _materials
  301. Local indices:=material.indices.Data
  302. For Local i:=0 Until material.indices.Length Step 3
  303. Local t:=indices[i]
  304. indices[i]=indices[i+1]
  305. indices[i+1]=t
  306. Next
  307. material.dirty|=Dirty.IndexBuffer
  308. Next
  309. End
  310. #rem monkeydoc Scales texture coordinates.
  311. #end
  312. Method ScaleTexCoords( scale:Vec2f )
  313. Local vertices:=_vertices.Data
  314. For Local i:=0 Until _vertices.Length
  315. vertices[i].texCoord0*=scale
  316. Next
  317. InvalidateVertices()
  318. End
  319. #rem monkeydoc Loads a mesh from a file.
  320. On its own, mojo3d can only load gltf2 format mesh and model files.
  321. To add more formats, #import the mojo3d-assimp module into your app, eg:
  322. ```
  323. #Import "<mojo3d>"
  324. #Import "<mojo3d-assimp>"
  325. ```
  326. This will allow you to load any format supported by the assimp module.
  327. However, importing the assimp module into your app will also increase its size.
  328. #end
  329. Function Load:Mesh( path:String )
  330. For Local loader:=Eachin Mojo3dLoader.Instances
  331. Local mesh:=loader.LoadMesh( path )
  332. If mesh Return mesh
  333. Next
  334. Return Null
  335. End
  336. Internal
  337. Method GetVertexBuffer:VertexBuffer()
  338. If _dirty & Dirty.VertexBuffer
  339. _vbuffer=New VertexBuffer( Vertex3f.Format,_vertices.Length )
  340. _vbuffer.SetVertices( _vertices.Data.Data,0,_vertices.Length )
  341. _dirty&=~Dirty.VertexBuffer
  342. End
  343. Return _vbuffer
  344. End
  345. Method GetIndexBuffer:IndexBuffer( materialid:Int )
  346. Local material:=_materials[materialid]
  347. If material.dirty & Dirty.IndexBuffer
  348. Local indices:=material.indices
  349. material.ibuffer=New IndexBuffer( IndexFormat.UINT32,indices.Length )
  350. material.ibuffer.SetIndices( indices.Data.Data,0,indices.Length )
  351. material.dirty&=~Dirty.IndexBuffer
  352. End
  353. Return material.ibuffer
  354. End
  355. Private
  356. Enum Dirty
  357. Bounds=1
  358. VertexBuffer=2
  359. IndexBuffer=4
  360. End
  361. class MaterialID
  362. Field indices:=New Stack<UInt>
  363. Field dirty:Dirty=Dirty.IndexBuffer
  364. Field ibuffer:IndexBuffer
  365. End
  366. Const IndexPitch:=4
  367. Field _vertices:=New Stack<Vertex3f>
  368. Field _materials:=New Stack<MaterialID>
  369. Field _dirty:Dirty=Null
  370. Field _bounds:Boxf=Boxf.EmptyBounds
  371. Field _vbuffer:VertexBuffer
  372. Field _minDirty:Int
  373. Field _maxDirty:Int
  374. Method InvalidateVertices( first:Int,count:Int )
  375. _minDirty=Min( _minDirty,first )
  376. _maxDirty=Max( _maxDirty,first+count )
  377. _dirty|=Dirty.Bounds|Dirty.VertexBuffer
  378. End
  379. Method InvalidateVertices()
  380. InvalidateVertices( 0,_vertices.Length )
  381. End
  382. Method GetMaterial:MaterialID( materialid:Int,dirty:Dirty=Dirty.IndexBuffer )
  383. DebugAssert( materialid>=0 And materialid<=_materials.Length,"Materialid out of range" )
  384. If materialid=_materials.Length AddMaterials( 1 )
  385. Local material:=_materials[materialid]
  386. material.dirty|=dirty
  387. Return material
  388. End
  389. End