123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669 |
- Namespace mojo3d
- #rem monkeydoc The Mesh class.
- #end
- Class Mesh Extends Resource
-
- #rem monkeydoc Creates a new mesh.
-
- Creates a new empty mesh.
-
- Meshes don't actual contain instances of materials. Instead, mesh triangles are added to 'logical' materials which are effectively just integer indices.
-
- Actual materials are stored in models, and can be accessed via the [[Model.Materials]] property.
-
- #end
- Method New()
- _dirty=Null
- _bounds=Boxf.EmptyBounds
- _vertices=New Stack<Vertex3f>
- _materials=New Stack<MaterialID>
- End
-
- Method New( vertices:Vertex3f[],indices:UInt[] )
- Self.New()
- AddVertices( vertices )
- AddTriangles( indices )
- End
-
- #rem monkeydoc Number of vertices.
- #end
- Property NumVertices:Int()
-
- Return _vertices.Length
- End
-
- #rem monkeydoc Total number of indices.
- #end
- Property NumIndices:int()
-
- Local n:=0
-
- For Local material:=Eachin _materials
- n+=material.indices.Length
- Next
-
- Return n
- End
-
- #rem monkeydoc Number of materials.
-
- This will always be at least one.
-
- #end
- Property NumMaterials:Int()
-
- Return _materials.Length
- End
- #rem monkeydoc Mesh bounding box.
- #end
- Property Bounds:Boxf()
-
- If _dirty & Dirty.Bounds
-
- _bounds=Boxf.EmptyBounds
-
- For Local i:=0 Until _vertices.Length
- _bounds|=_vertices[i].position
- Next
-
- _dirty&=~Dirty.Bounds
- Endif
-
- Return _bounds
- End
-
- #rem monkeydoc Compacts the mesh.
-
- Compacts all internal data used by the mesh so they occupy as little memory as possible.
-
- #end
- Method Compact()
-
- _vertices.Compact()
- _materials.Compact()
-
- For Local material:=Eachin _materials
- material.indices.Compact()
- Next
- End
-
- #rem monkeydoc Clears the mesh.
-
- Removes all vertices and primitives from the mesh, and resets the number of logical materials to '1'.
-
- #end
- Method Clear()
-
- _dirty=Null
- _bounds=Boxf.EmptyBounds
- _vertices.Clear()
- _materials.Clear()
-
- InvalidateVertices()
- End
-
- #rem monkeydoc Clear the mesh vertices.
- #end
- Method ClearVertices()
-
- ResizeVertices( 0 )
- End
-
- Method ResizeVertices( length:Int )
-
- _vertices.Resize( length )
- InvalidateVertices()
- End
- #rem monkeydoc Sets a range of vertices.
- #end
- Method SetVertices( vertices:Vertex3f Ptr,first:Int,count:Int )
-
- DebugAssert( first>=0 And count>=0 And first<=_vertices.Length And first+count<=_vertices.Length,"Invalid vertex range" )
-
- libc.memcpy( _vertices.Data.Data+first,vertices,count*Vertex3f.Pitch )
- InvalidateVertices( first,count )
- End
-
- Method SetVertices( vertices:Vertex3f[] )
-
- _vertices.Resize( vertices.Length )
-
- SetVertices( vertices.Data,0,vertices.Length )
- End
-
- #rem monkeydoc Sets a single vertex.
- #end
- Method SetVertex( index:Int,vertex:Vertex3f )
-
- DebugAssert( index>=0 And index<_vertices.Length,"Vertex index out of range" )
-
- _vertices[index]=vertex
-
- InvalidateVertices( index,1 )
- End
-
- #rem monkeydoc Adds vertices.
- #end
- Method AddVertices( vertices:Vertex3f Ptr,count:Int )
-
- Local first:=_vertices.Length
-
- _vertices.Resize( first+count )
-
- libc.memcpy( _vertices.Data.Data+first,vertices,count*Vertex3f.Pitch )
-
- InvalidateVertices( first,count )
- End
- Method AddVertices( vertices:Vertex3f[] )
-
- AddVertices( vertices.Data,vertices.Length )
- End
-
- #rem monkeydoc Adds a single vertex.
- #end
- Method AddVertex( vertex:Vertex3f )
-
- AddVertices( Varptr vertex,1 )
- End
-
- #rem monkeydoc Gets all vertices as an array.
- #end
- Method GetVertices:Vertex3f[]()
-
- Return _vertices.ToArray()
- End
-
- #rem monkeydoc Gets a single vertex.
- #end
- Method GetVertex:Vertex3f( index:Int )
-
- DebugAssert( index>=0 And index<_vertices.Length,"Vertex index out of range" )
-
- Return _vertices[index]
- End
-
- #rem monkeydoc Sets tthe triangles for a material in the mesh.
-
- `materialid` must be a valid material id in the range 0 to [[NumMaterials]] inclusive.
-
- #end
- Method SetTriangles( indices:UInt Ptr,first:Int,count:Int,materialid:Int=0 )
-
- DebugAssert( first Mod 3=0,"First must be a multiple of 3" )
- DebugAssert( count Mod 3=0,"Count must be a multiple of 3" )
-
- Local mindices:=GetMaterial( materialid ).indices
- DebugAssert( first>=0 And count>=0 And first<=mindices.Length And first+count<=mindices.Length,"Invalid range" )
-
- libc.memcpy( mindices.Data.Data+first,indices,count*IndexPitch )
- End
-
- Method SetTriangles( indices:UInt[],materialid:Int=0 )
-
- SetTriangles( indices.Data,indices.Length,materialid )
- End
-
- Method SetTriangle( index:Int,i0:Int,i1:Int,i2:Int,materialid:Int=0 )
-
- DebugAssert( index Mod 3=0,"Index must be a multiple of 3" )
-
- Local mindices:=GetMaterial( materialid ).indices
-
- DebugAssert( index>=0 And index+3<=mindices.Length,"Triangle index out of range" )
-
- mindices[index]=i0;mindices[index+1]=i1;mindices[index+2]=i2
- End
-
- #rem monkeydoc Adds triangles to the mesh.
- `count` is the number of indices to add and must be a multiple of 3.
-
- `materialid` must be a valid material id in the range 0 to [[NumMaterials]] inclusive.
-
- If `materialid` is equal to NumMaterials, a new material is automatically added first.
-
- #end
- Method AddTriangles( indices:UInt Ptr,count:Int,materialid:Int=0 )
-
- DebugAssert( count Mod 3=0,"Count must be a multiple of 3" )
-
- Local mindices:=GetMaterial( materialid ).indices
-
- Local first:=mindices.Length
-
- mindices.Resize( mindices.Length+count )
-
- libc.memcpy( mindices.Data.Data+first,indices,count*IndexPitch )
- End
-
- Method AddTriangles( indices:UInt[],materialid:Int=0 )
-
- AddTriangles( indices.Data,indices.Length,materialid )
- End
- #rem monkeydoc Adds a single triangle the mesh.
- #end
- Method AddTriangle( i0:UInt,i1:UInt,i2:UInt,materialid:Int=0 )
-
- Local indices:=GetMaterial( materialid ).indices
-
- indices.Add( i0 )
- indices.Add( i1 )
- indices.Add( i2 )
- End
-
- #rem monkeydoc Get indices for a material id.
- #end
- Method GetIndices:UInt[]( materialid:Int=0 )
-
- DebugAssert( materialid>=0 And materialid<_materials.Length,"Material id out of range" )
-
- Return _materials[materialid].indices.ToArray()
- End
-
- Method GetAllIndices:Uint[]()
-
- Local indices:=New Uint[NumIndices],ip:=indices.Data
-
- For Local material:=Eachin _materials
- Local mindices:=material.indices
- libc.memcpy( ip,mindices.Data.Data,mindices.Length*IndexPitch )
- ip+=mindices.Length
- Next
-
- Return indices
- End
- #rem monkeydoc Adds materials to the mesh.
-
- Adds `count` logical materials to the mesh.
-
- Returns the first material id of the newly added materials.
-
- #end
- Method AddMaterials:Int( count:Int )
-
- Local first:=_materials.Length
-
- For Local i:=0 Until count
- _materials.Push( New MaterialID )
- Next
-
- Return first
- End
-
- #rem monkeydoc Adds a mesh to this mesh.
- #end
- Method AddMesh( mesh:Mesh,materialid:Int=0 )
-
- Local v0:=_vertices.Length
-
- AddVertices( mesh._vertices.Data.Data,mesh._vertices.Length )
-
- For Local material:=Eachin mesh._materials
-
- Local count:=material.indices.Length
-
- Local mindices:=material.indices.Data
-
- If v0
- Local indices:=New UInt[count]
- For Local i:=0 Until count
- indices[i]=mindices[i]+v0
- Next
- mindices=indices
- Endif
-
- AddTriangles( mindices.Data,count,materialid )
-
- materialid+=1
- Next
-
- End
-
- #rem monkeydoc Transforms all vertices in the mesh.
- #end
- Method TransformVertices( matrix:AffineMat4f )
-
- Local vertices:=_vertices.Data
-
- Local cofactor:=matrix.m.Cofactor()
-
- For Local i:=0 Until _vertices.Length
-
- vertices[i].position=matrix * vertices[i].position
-
- vertices[i].normal=(cofactor * vertices[i].normal).Normalize()
-
- vertices[i].tangent.XYZ=(cofactor * vertices[i].tangent.XYZ).Normalize()
- Next
-
- InvalidateVertices()
- End
-
- #rem monkeydoc Fits all vertices in the mesh to a box.
- #end
- Method FitVertices( box:Boxf,uniform:Bool=True )
- Local bounds:=Bounds
-
- Local scale:=box.Size/bounds.Size
-
- If uniform scale=New Vec3f( Min( scale.x,Min( scale.y,scale.z ) ) )
-
- Local m:=Mat3f.Scaling( scale )
-
- Local t:=box.Center - m * bounds.Center
-
- TransformVertices( New AffineMat4f( m,t ) )
- End
-
- #rem monkeydoc Updates mesh normals.
-
- Recalculates all vertex normals based on triangle and vertex positions.
-
- #end
- Method UpdateNormals()
- Local vertices:=_vertices.Data
-
- For Local i:=0 Until _vertices.Length
-
- vertices[i].normal=New Vec3f(0)
- Next
-
- For Local material:=Eachin _materials
-
- Local indices:=material.indices.Data
-
- For Local i:=0 Until material.indices.Length Step 3
-
- Local i1:=indices[i+0]
- Local i2:=indices[i+1]
- Local i3:=indices[i+2]
-
- Local v1:=vertices[i1].position
- Local v2:=vertices[i2].position
- Local v3:=vertices[i3].position
-
- Local n:=(v2-v1).Cross(v3-v1).Normalize()
-
- vertices[i1].normal+=n
- vertices[i2].normal+=n
- vertices[i3].normal+=n
-
- Next
-
- Next
-
- For Local i:=0 Until _vertices.Length
-
- vertices[i].normal=vertices[i].normal.Normalize()
- Next
- InvalidateVertices()
- End
- #rem monkeydoc Updates mesh tangents.
-
- Recalculates all vertex tangents based on triangles, vertex normals and vertex texcoord0.
-
- #end
- Method UpdateTangents()
-
- Local vertices:=_vertices.Data.Data
-
- Local tan1:=New Vec3f[_vertices.Length]
- Local tan2:=New Vec3f[_vertices.Length]
-
- For Local material:=Eachin _materials
-
- Local indices:=material.indices.Data
-
- For Local i:=0 Until material.indices.Length Step 3
-
- Local i1:=indices[i+0]
- Local i2:=indices[i+1]
- Local i3:=indices[i+2]
-
- Local v1:=vertices+i1
- Local v2:=vertices+i2
- Local v3:=vertices+i3
-
- Local x1:=v2->Tx-v1->Tx
- Local x2:=v3->Tx-v1->Tx
- Local y1:=v2->Ty-v1->Ty
- Local y2:=v3->Ty-v1->Ty
- Local z1:=v2->Tz-v1->Tz
- Local z2:=v3->Tz-v1->Tz
-
- Local s1:=v2->Sx-v1->Sx
- Local s2:=v3->Sx-v1->Sx
- Local t1:=v2->Sy-v1->Sy
- Local t2:=v3->Sy-v1->Sy
-
- Local r:=1.0/(s1*t2-s2*t1)
-
- Local sdir:=New Vec3f( (t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r )
- Local tdir:=New Vec3f( (s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r )
-
- tan1[i1]+=sdir
- tan1[i2]+=sdir
- tan1[i3]+=sdir
-
- tan2[i1]+=tdir
- tan2[i2]+=tdir
- tan2[i3]+=tdir
- Next
-
- Next
-
- For Local i:=0 Until _vertices.Length
-
- Local v:=vertices+i
-
- Local n:=v->normal,t:=tan1[i]
-
- v->tangent.XYZ=( t - n * n.Dot( t ) ).Normalize()
-
- v->tangent.w=n.Cross( t ).Dot( tan2[i] ) < 0 ? -1 Else 1
- Next
-
- InvalidateVertices()
- End
-
- #rem monkeydoc Flips all triangles.
- #end
- Method FlipTriangles()
-
- For Local material:=Eachin _materials
-
- Local indices:=material.indices.Data
-
- For Local i:=0 Until material.indices.Length Step 3
- Local t:=indices[i]
- indices[i]=indices[i+1]
- indices[i+1]=t
- Next
-
- material.dirty|=Dirty.IndexBuffer
- Next
-
- End
-
- #rem monkeydoc Scales texture coordinates.
- #end
- Method ScaleTexCoords( scale:Vec2f )
-
- Local vertices:=_vertices.Data
-
- For Local i:=0 Until _vertices.Length
-
- vertices[i].texCoord0*=scale
- Next
-
- InvalidateVertices()
- End
- #rem monkeydoc Copies texcoord0 to texcoord1.
- #end
- Method CopyTexCoords()
- Local vertices:=_vertices.Data
-
- For Local i:=0 Until _vertices.Length
-
- vertices[i].texCoord1=vertices[i].texCoord0
- Next
-
- InvalidateVertices()
- End
-
- #rem monkeydoc Loads a mesh from a file.
-
- On its own, mojo3d can only load gltf2 format mesh and model files.
-
- To add more formats, #import the mojo3d-assimp module into your app, eg:
-
- ```
- #Import "<mojo3d>"
- #Import "<mojo3d-assimp>"
- ```
-
- This will allow you to load any format supported by the assimp module.
-
- However, importing the assimp module into your app will also increase its size.
-
- #end
- Function Load:Mesh( path:String )
-
- For Local loader:=Eachin Mojo3dLoader.Instances
-
- Local mesh:=loader.LoadMesh( path )
- If mesh Return mesh
-
- Next
-
- Return Null
- End
-
- Internal
-
- Method GetVertexBuffer:VertexBuffer()
-
- If _dirty & Dirty.VertexBuffer
-
- _vbuffer=New VertexBuffer( Vertex3f.Format,_vertices.Length )
-
- _vbuffer.SetVertices( _vertices.Data.Data,0,_vertices.Length )
-
- _dirty&=~Dirty.VertexBuffer
- End
-
- Return _vbuffer
- End
-
- Method GetIndexBuffer:IndexBuffer( materialid:Int )
-
- Local material:=_materials[materialid]
-
- If material.dirty & Dirty.IndexBuffer
-
- Local indices:=material.indices
-
- material.ibuffer=New IndexBuffer( IndexFormat.UINT32,indices.Length )
-
- material.ibuffer.SetIndices( indices.Data.Data,0,indices.Length )
-
- material.dirty&=~Dirty.IndexBuffer
- End
-
- Return material.ibuffer
- End
-
- Method GetIndexBufferBounds:Boxf( materialid:Int )
-
- Local material:=_materials[materialid]
-
- If material.dirty & Dirty.Bounds
-
- Local bounds:=Bounds.EmptyBounds
-
- Local vertices:=_vertices.Data.Data
-
- For Local i:=Eachin material.indices
-
- bounds|=vertices[i].position
- Next
-
- material.bounds=bounds
-
- material.dirty&=~Dirty.Bounds
- Endif
-
- Return material.bounds
- End
-
-
- Private
-
- Enum Dirty
- Bounds=1
- VertexBuffer=2
- IndexBuffer=4
- End
-
- class MaterialID
- Field indices:=New Stack<UInt>
- Field dirty:Dirty=Dirty.IndexBuffer|Dirty.Bounds
- Field ibuffer:IndexBuffer
- Field bounds:Boxf
- End
-
- Const IndexPitch:=4
-
- Field _vertices:=New Stack<Vertex3f>
- Field _materials:=New Stack<MaterialID>
-
- Field _dirty:Dirty=Null
- Field _bounds:Boxf=Boxf.EmptyBounds
- Field _vbuffer:VertexBuffer
- Field _minDirty:Int
- Field _maxDirty:Int
-
- Method InvalidateVertices( first:Int,count:Int )
-
- _minDirty=Min( _minDirty,first )
- _maxDirty=Max( _maxDirty,first+count )
-
- _dirty|=Dirty.Bounds|Dirty.VertexBuffer
- End
-
- Method InvalidateVertices()
-
- InvalidateVertices( 0,_vertices.Length )
- End
-
- Method GetMaterial:MaterialID( materialid:Int,dirty:Dirty=Dirty.IndexBuffer|Dirty.Bounds )
-
- DebugAssert( materialid>=0 And materialid<=_materials.Length,"Materialid out of range" )
-
- If materialid=_materials.Length AddMaterials( 1 )
-
- Local material:=_materials[materialid]
-
- material.dirty|=dirty
- Return material
- End
-
- End
|