Code archives/3D Graphics - Mesh/miniB3D - .x mesh loader
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
Hi folks, I wanted to share my .x mesh loader for minib3d just in case someone needs one as well ;) Install instructions: (1) Save the source to sidesign.mod/minib3d.mod/inc/TXLoader.bmx (2) Open minib3d.bmx and add the line Include "inc/TXLoader.bmx" (3) Open inc/TMesh.bmx and add the line If Right(String(file), 2) = ".x" Then Return TXLoader.LoadXMesh(file, parent_ent) (4) Compile modules - Done. Notice: Works with plaintext .x files only, plus the version must be 3.2 (0302) | |||||
Rem Direct3D .x Mesh Loader * Version 2012-FEB-13 12:05:00 * License: Free for commercial and non-commercial use (do anything you want with it) * Contributing authors: * ZaP [ contact (at) starfare (dot) eu ] * Changelog: 2012-FEB-06: * Release 2012-FEB-13: * Frame transformation matrices read & applied correctly * Fixed texture loading, textures are now loaded from the directory the meshfile is loaded from * Fixed multiple creation of the same sub-mesh End Rem Private Type XLoader_TreeNode Field children:TList Field content:String Field template:String Field name:String Method New() Self.children = New TList End Method Method Add(element:XLoader_TreeNode) Self.children.AddLast(element) End Method End Type Public Type TXLoader Function LoadXMesh:TMesh(path:String, parent:TEntity = Null) Local s:TStream = ReadFile(path) If Not s Then Return CreateCube() ; Local header:String = s.ReadString(4) Local version:String = s.ReadString(4) Local format:String = s.ReadString(4) Local floatsize:Int = Int(s.ReadString(4)) If header = "xof " If version = "0302" If format = "txt " Local submesh:TMesh = CreateMesh() While Not s.Eof() ' check first byte Local checkbyte:Int = s.ReadByte() s.Seek(s.Pos() - 1) If Not (XLoader_isWhitespace(Chr(checkbyte)) Or checkbyte = Asc("/") Or checkbyte = Asc("#")) Local read:String = s.ReadString(s.Size() - s.Pos()) read = XLoader_RemoveUnprintables(read) read = read.Replace(" {", "{") read = read.Replace("{ ", "{") read = read.Replace(" }", "}") read = read.Replace("} ", "}") Local tree:XLoader_TreeNode = XLoader_MakeTree(read) ' fetch material data Local matlist:TList = XLoader_FindTreeElements(tree, "material") Local material:XLoader_TreeNode Local brushes:TBrush[matlist.Count()] Local brushnames:String[matlist.Count()] Local count:Int = 0 For material = EachIn matlist brushnames[count] = material.name Local brushrgba:String[] = material.content[..material.content.Find(";;")].Split(";") brushes[count] = CreateBrush(Float(brushrgba[0]) * 255.0, Float(brushrgba[1]) * 255.0, Float(brushrgba[2]) * 255.0) brushes[count].BrushAlpha(Float(brushrgba[3])) Local texturefilestart:Int = material.content.Find("~q") If texturefilestart <> - 1 Local texturefilename:String = material.content[texturefilestart + 1..material.content.Find("~q", texturefilestart + 1)] Local tex:TTexture = LoadTexture(XLoader_ExtractFilePath(path) + "/" + texturefilename) If tex <> Null Then brushes[count].BrushTexture(tex) End If count:+1 Next Local framelist:TList = XLoader_FindTreeElements(tree, "frame") Local frametree:XLoader_TreeNode For frametree = EachIn framelist If frametree.name.ToLower() <> "world" ' read transformation matrix Local tmatlist:TList = XLoader_FindTreeElements(frametree, "FrameTransformMatrix") ' assemble mesh Local meshnodes:TList = XLoader_FindTreeElements(frametree, "mesh") Local meshnode:XLoader_TreeNode Local currentmeshnode:Int = 0 Local tformmat:TMatrix For meshnode = EachIn meshnodes ' create corresponding tformmatrix Local meshlistelm:Object = tmatlist.ValueAtIndex(currentmeshnode) If meshlistelm <> Null Local tmat:String[] = XLoader_TreeNode(meshlistelm).content.Split(",") currentmeshnode:+1 tformmat:TMatrix = New TMatrix tformmat.LoadIdentity() Local maty:Int = -1 Local matx:Int = 0 For matx = 0 To 15 If matx Mod 4 = 0 Then maty:+1 tformmat.grid[matx - maty * 4, maty] = Float(tmat[matx]) Next EndIf ' create surface Local surf:TSurface = CreateSurface(submesh) Local meshdata:String = meshnode.content.Trim() meshdata.Replace(" ", "") ' read vertex count Local offset:Int = meshdata.Find(";") Local vertexcount:Int = Int(meshdata[..offset]) meshdata = meshdata[offset + 1..] offset = meshdata.Find(";;") Local vertexdata:String[] = meshdata[..offset].Replace(",", "").Split(";") ' add vertices Local vcount:Int = 0 For vcount = 0 To (vertexcount - 1) * 3 Step 3 Local vertx:Float = Float(vertexdata[vcount]) Local verty:Float = Float(vertexdata[vcount + 1]) Local vertz:Float = Float(vertexdata[vcount + 2]) Local x:Float = vertx * tformmat.grid[0, 0] + verty * tformmat.grid[0, 1] + vertz * tformmat.grid[0, 2] + tformmat.grid[0, 3] Local y:Float = vertx * tformmat.grid[1, 0] + verty * tformmat.grid[1, 1] + vertz * tformmat.grid[1, 2] + tformmat.grid[1, 3] Local z:Float = vertx * tformmat.grid[2, 0] + verty * tformmat.grid[2, 1] + vertz * tformmat.grid[2, 2] + tformmat.grid[2, 3] Local w:Float = tformmat.grid[3, 0] + tformmat.grid[3, 1] + tformmat.grid[3, 2] + tformmat.grid[3, 3] AddVertex(surf, x, y, z) Next ' read face count meshdata = meshdata[offset + 2..] offset = meshdata.Find(";") Local facecount:Int = Int(meshdata[..offset]) meshdata = meshdata[offset + 1..] offset = meshdata.Find(";;") Local facedata:String[] = meshdata[..offset].Replace(";,", ";").Split(";") ' draw faces Local fcount:Int = 0 For fcount = 1 To facecount * 2 Step 2 Local faces:String[] = facedata[fcount].Split(",") If faces.Length = 3 surf.AddTriangle(Int(faces[0]), Int(faces[1]), Int(faces[2])) ElseIf faces.Length = 4 surf.AddTriangle(Int(faces[0]), Int(faces[1]), Int(faces[2])) surf.AddTriangle(Int(faces[2]), Int(faces[3]), Int(faces[0])) EndIf Next ' lookup mesh normals Local normals:TList = XLoader_FindTreeElements(meshnode, "meshnormals") If normals.IsEmpty() UpdateNormals(submesh) Else Local normaldata:String = XLoader_TreeNode(normals.ValueAtIndex(0)).content Local normalcount:Int = Int(normaldata[..normaldata.Find(";")]) normaldata = normaldata[normaldata.Find(";") + 1..normaldata.Find(";;")].Replace(",", "") Local normalbits:String[] = normaldata.Split(";") Local i:Int = 0 For i = 0 To normalcount - 1 surf.VertexNormal(i, Float(normalbits[i * 3]), Float(normalbits[i * 3 + 1]), Float(normalbits[i * 3 + 2])) Next EndIf ' texture coordinates Local textcoords:TList = XLoader_FindTreeElements(meshnode, "MeshTextureCoords") If Not textcoords.IsEmpty() Local texturecoordsdata:String = XLoader_TreeNode(textcoords.ValueAtIndex(0)).content Local texturecoordscount:Int = Int(texturecoordsdata[..texturecoordsdata.Find(";")]) texturecoordsdata = texturecoordsdata[texturecoordsdata.Find(";") + 1..texturecoordsdata.Find(";;")] Local tcoords:String[] = texturecoordsdata.Replace(",", "").Split(";") Local i:Int = 0 For i = 0 To texturecoordscount - 1 surf.VertexTexCoords(i, Float(tcoords[i * 2]), Float(tcoords[i * 2 + 1])) Next EndIf ' apply materials Local texlist:TList = XLoader_FindTreeElements(meshnode, "MeshMaterialList") If Not texlist.IsEmpty() Local texdata:String = XLoader_TreeNode(texlist.ValueAtIndex(0)).content texdata = texdata[texdata.Find(";;") + 3..] Local texname:String = texdata[..texdata.Find("}")] Local i:Int = 0 For i = 0 To brushnames.Length - 1 If brushnames[i] = texname Then surf.PaintSurface(brushes[i]) Next End If Next EndIf Next Else If XLoader_isWhitespace(checkbyte) s.Seek(s.Pos() + 1) Else s.ReadLine() End If EndIf Wend s.Close() Return submesh Else DebugLog "X Mesh Loader: Unsupported format '" + format + "'!" EndIf Else DebugLog "X Mesh Loader: Unsupported version '" + version + "'!" EndIf Else DebugLog "X Mesh Loader: Invalid x-mesh!" EndIf Return CreateCube() End Function Function XLoader_ExtractFilePath:String(path:String) Local i : Int = 0 For i = Len(path)-1 To 0 Step -1 If Chr(path[i]) = "/" Or Chr(path[i]) = "\" Then Return path[..i] Next Return path End Function Function XLoader_FindTreeElements:TList(tree:XLoader_TreeNode, template:String) Local l:TList = New TList If Lower(tree.template) = template l.AddLast(tree) EndIf Local element:XLoader_TreeNode For element = EachIn tree.children If element.template = template Then l.AddLast(element) If Not element.children.IsEmpty() Then l = XLoader_JoinLists(l, XLoader_FindTreeElements(XLoader_TreeNode(element), template)) Next Return l End Function Function XLoader_JoinLists:TList(l1:TList, l2:TList) Local o:Object For o = EachIn l2 l1.AddLast(o) Next Return l1 End Function Function XLoader_MakeTree:XLoader_TreeNode(s:String, parent:XLoader_TreeNode = Null) Local root:XLoader_TreeNode = New XLoader_TreeNode Local pointer:Int = 0 Local find:Int = 0 s = s.Trim() While s.Length > 0 find = s.find("{", pointer) If find = -1 Then Exit Local header:String = s[pointer..find] Local bits:String[] = header.Split(" ") pointer = find + 1 root.template = bits[0] If bits.Length > 1 Then root.name = bits[1] Local frameend:Int = XLoader_FindBracketMatch(s[pointer..]) Local framecontent:String = s[pointer..frameend + pointer] root.content = framecontent s = s[pointer + 1 + frameend..] Select root.template.ToLower() Case "frame" Local newNode:XLoader_TreeNode = XLoader_MakeTree(framecontent, root) If newNode Then root.Add(newNode) Case "mesh" Local contentend:Int = XLoader_FindAlphaChar(framecontent) root.content = framecontent[..contentend] Local newNode:XLoader_TreeNode = XLoader_MakeTree(framecontent[contentend..], root) If newNode Then root.Add(newNode) Default Local newNode:XLoader_TreeNode = XLoader_MakeTree(s, root) If newNode Then root.Add(newNode) Exit End Select pointer = 0 Wend Return root End Function Function XLoader_FindAlphaChar:Int(s:String) Local i:Int = 0 For i = 0 To s.Length - 1 If s[i] >= 65 And s[i] <= 90 Then Return i ' A-Z If s[i] >= 97 And s[i] <= 122 Then Return i ' a-z Next Return - 1 End Function Function XLoader_FindBracketMatch:Int(s:String) Local index:Int = 1 Local i:Int = 0 Local ascbracket:Byte[2] ascbracket[0] = Asc("{") ascbracket[1] = Asc("}") For i = 0 To s.Length - 1 Select s[i] Case ascbracket[0] index:+1 Case ascbracket[1] index:-1 End Select If index = 0 Then Return i Next Return - 1 End Function Function XLoader_CharCount:Int(s:String, find:String) Local count:Int = 0 Local i:Int = 0 Local ascfind:Byte = Asc(find) For i = 0 To s.Length - 1 If s[i] = ascfind Then count:+1 Next Return count End Function Function XLoader_RemoveUnprintables:String(s:String) Local i:Int = 0 For i = 0 To 31 s = s.Replace(Chr(i), "") Next Return s End Function Function XLoader_isWhitespace:Byte(char:String) Return Asc(char) <= 32 End Function End Type |
Comments
None.
Code Archives Forum