From 77e62588fec7e1bccaf33dbe22fd8be36516c5ba Mon Sep 17 00:00:00 2001 From: bloeys Date: Thu, 18 Nov 2021 21:02:14 +0400 Subject: [PATCH] Full mesh parsing --- asig/asig.go | 251 +++++++++++++++++++++++++++++++++++++++++++++++--- asig/enums.go | 23 +++++ asig/mesh.go | 87 +++++++++++++++++ go.mod | 2 + go.sum | 2 + main.go | 12 ++- 6 files changed, 364 insertions(+), 13 deletions(-) create mode 100755 asig/enums.go create mode 100755 asig/mesh.go create mode 100755 go.sum diff --git a/asig/asig.go b/asig/asig.go index e91ce44..ff501cc 100755 --- a/asig/asig.go +++ b/asig/asig.go @@ -10,17 +10,18 @@ package asig //Functions struct aiScene* aiImportFile(const char* pFile, unsigned int pFlags); + */ import "C" -import "unsafe" +import ( + "unsafe" + + "github.com/bloeys/gglm/gglm" +) type Node struct { } -type Mesh struct { - Vertices uint -} - type Material struct { } @@ -58,31 +59,259 @@ func ImportFile(file string, postProcessFlags uint) *Scene { cs := C.aiImportFile(cstr, C.uint(postProcessFlags)) scene := parseScene(cs) - println("Num verts:", scene.Meshes[0].Vertices, "; Meshes:", len(scene.Meshes)) - return &Scene{} + return scene } func parseScene(cs *C.struct_aiScene) *Scene { s := &Scene{} - s.Meshes = parseMeshes(*cs.mMeshes, uint(cs.mNumMeshes)) + s.Meshes = parseMeshes(cs.mMeshes, uint(cs.mNumMeshes)) return s } -func parseMeshes(cm *C.struct_aiMesh, count uint) []*Mesh { +func parseMeshes(cm **C.struct_aiMesh, count uint) []*Mesh { + + if cm == nil { + return []*Mesh{} + } meshes := make([]*Mesh, count) - cmeshes := unsafe.Slice(&cm, count) + cmeshes := unsafe.Slice(cm, count) for i := 0; i < int(count); i++ { m := &Mesh{} - m.Vertices = uint(cmeshes[i].mNumVertices) + + cmesh := cmeshes[i] + vertCount := uint(cmesh.mNumVertices) + + m.Vertices = parseVec3s(cmesh.mVertices, vertCount) + m.Normals = parseVec3s(cmesh.mNormals, vertCount) + m.Tangents = parseVec3s(cmesh.mTangents, vertCount) + m.BitTangents = parseVec3s(cmesh.mBitangents, vertCount) + + //Color sets + m.ColorSets = parseColorSet(cmesh.mColors, vertCount) + + //Tex coords + m.TexCoords = parseTexCoords(cmesh.mTextureCoords, vertCount) + m.TexCoordChannelCount = [8]uint{} + for j := 0; j < len(cmesh.mTextureCoords); j++ { + + //If a color set isn't available then it is nil + if cmesh.mTextureCoords[j] == nil { + continue + } + + m.TexCoordChannelCount[j] = uint(cmeshes[j].mNumUVComponents[j]) + } + + //Faces + cFaces := unsafe.Slice(cmesh.mFaces, cmesh.mNumFaces) + m.Faces = make([]Face, cmesh.mNumFaces) + for j := 0; j < len(m.Faces); j++ { + + m.Faces[j] = Face{ + Indices: parseUInts(cFaces[j].mIndices, uint(cFaces[j].mNumIndices)), + } + } + + //Other + m.Bones = parseBones(cmesh.mBones, uint(cmesh.mNumBones)) + m.AnimMeshes = parseAnimMeshes(cmesh.mAnimMeshes, uint(cmesh.mNumAnimMeshes)) + m.AABB = AABB{ + Min: parseVec3(&cmesh.mAABB.mMin), + Max: parseVec3(&cmesh.mAABB.mMax), + } + + m.MorphMethod = MorphMethod(cmesh.mMethod) + m.MaterialIndex = uint(cmesh.mMaterialIndex) + m.Name = parseAiString(cmesh.mName) meshes[i] = m } return meshes } + +func parseVec3(cv *C.struct_aiVector3D) gglm.Vec3 { + return gglm.Vec3{ + Data: [3]float32{ + float32(cv.x), + float32(cv.y), + float32(cv.z), + }, + } +} + +func parseAnimMeshes(cam **C.struct_aiAnimMesh, count uint) []*AnimMesh { + + if cam == nil { + return []*AnimMesh{} + } + + animMeshes := make([]*AnimMesh, count) + cAnimMeshes := unsafe.Slice(cam, count) + + for i := 0; i < int(count); i++ { + + m := cAnimMeshes[i] + animMeshes[i] = &AnimMesh{ + Name: parseAiString(m.mName), + Vertices: parseVec3s(m.mVertices, uint(m.mNumVertices)), + Normals: parseVec3s(m.mNormals, uint(m.mNumVertices)), + Tangents: parseVec3s(m.mTangents, uint(m.mNumVertices)), + BitTangents: parseVec3s(m.mBitangents, uint(m.mNumVertices)), + Colors: parseColorSet(m.mColors, uint(m.mNumVertices)), + TexCoords: parseTexCoords(m.mTextureCoords, uint(m.mNumVertices)), + Weight: float32(m.mWeight), + } + } + + return animMeshes +} + +func parseTexCoords(ctc [MaxTexCoords]*C.struct_aiVector3D, vertCount uint) [MaxTexCoords][]gglm.Vec3 { + + texCoords := [MaxTexCoords][]gglm.Vec3{} + + for j := 0; j < len(ctc); j++ { + + //If a color set isn't available then it is nil + if ctc[j] == nil { + continue + } + + texCoords[j] = parseVec3s(ctc[j], vertCount) + } + + return texCoords +} + +func parseColorSet(cc [MaxColorSets]*C.struct_aiColor4D, vertCount uint) [MaxColorSets][]gglm.Vec4 { + + colorSet := [MaxColorSets][]gglm.Vec4{} + for j := 0; j < len(cc); j++ { + + //If a color set isn't available then it is nil + if cc[j] == nil { + continue + } + + colorSet[j] = parseColors(cc[j], vertCount) + } + + return colorSet +} + +func parseBones(cbs **C.struct_aiBone, count uint) []*Bone { + + if cbs == nil { + return []*Bone{} + } + + bones := make([]*Bone, count) + cbones := unsafe.Slice(cbs, count) + + for i := 0; i < int(count); i++ { + + cBone := cbones[i] + bones[i] = &Bone{ + Name: parseAiString(cBone.mName), + Weights: parseVertexWeights(cBone.mWeights, uint(cBone.mNumWeights)), + OffsetMatrix: parseMat4(&cBone.mOffsetMatrix), + } + } + + return bones +} + +func parseMat4(cm4 *C.struct_aiMatrix4x4) gglm.Mat4 { + + return gglm.Mat4{ + Data: [4][4]float32{ + {float32(cm4.a1), float32(cm4.b1), float32(cm4.c1), float32(cm4.d1)}, + {float32(cm4.a2), float32(cm4.b2), float32(cm4.c2), float32(cm4.d2)}, + {float32(cm4.a3), float32(cm4.b3), float32(cm4.c3), float32(cm4.d3)}, + {float32(cm4.a4), float32(cm4.b4), float32(cm4.c4), float32(cm4.d4)}, + }, + } +} + +func parseVertexWeights(cWeights *C.struct_aiVertexWeight, count uint) []VertexWeight { + + vw := make([]VertexWeight, count) + cvw := unsafe.Slice(cWeights, count) + + for i := 0; i < int(count); i++ { + + vw[i] = VertexWeight{ + VertIndex: uint(cvw[i].mVertexId), + Weight: float32(cvw[i].mWeight), + } + } + + return vw +} + +func parseAiString(aiString C.struct_aiString) string { + return C.GoStringN(&aiString.data[0], C.int(aiString.length)) +} + +func parseUInts(cui *C.uint, count uint) []uint { + + if cui == nil { + return []uint{} + } + + uints := make([]uint, count) + cUInts := unsafe.Slice(cui, count) + for i := 0; i < len(cUInts); i++ { + uints[i] = uint(cUInts[i]) + } + + return uints +} + +func parseVec3s(cv *C.struct_aiVector3D, count uint) []gglm.Vec3 { + + if cv == nil { + return []gglm.Vec3{} + } + + carr := unsafe.Slice(cv, count) + verts := make([]gglm.Vec3, count) + + for i := 0; i < int(count); i++ { + verts[i] = gglm.Vec3{ + Data: [3]float32{ + float32(carr[i].x), + float32(carr[i].y), + float32(carr[i].z), + }, + } + } + + return verts +} + +func parseColors(cv *C.struct_aiColor4D, count uint) []gglm.Vec4 { + + carr := unsafe.Slice(cv, count) + verts := make([]gglm.Vec4, count) + + for i := 0; i < int(count); i++ { + verts[i] = gglm.Vec4{ + Data: [4]float32{ + float32(carr[i].r), + float32(carr[i].g), + float32(carr[i].b), + float32(carr[i].a), + }, + } + } + + return verts +} diff --git a/asig/enums.go b/asig/enums.go new file mode 100755 index 0000000..a4a9c16 --- /dev/null +++ b/asig/enums.go @@ -0,0 +1,23 @@ +package asig + +type PrimitiveType int32 + +//Specifies types of primitives that can be present in a mesh +const ( + PrimitiveTypePoint = 1 << 0 + PrimitiveTypeLine = 1 << 1 + PrimitiveTypeTriangle = 1 << 2 + PrimitiveTypePolygon = 1 << 3 +) + +type MorphMethod int32 + +//Supported methods of mesh morphing +const ( + //Interpolation between morph targets + MorphMethodVertexBlend = 0x1 + //Normalized morphing between morph targets + MorphMethodMorphNormalized = 0x2 + //Relative morphing between morph targets + MorphMethodMorphRelative = 0x3 +) diff --git a/asig/mesh.go b/asig/mesh.go new file mode 100755 index 0000000..a7e46ee --- /dev/null +++ b/asig/mesh.go @@ -0,0 +1,87 @@ +package asig + +import "github.com/bloeys/gglm/gglm" + +const ( + MaxColorSets = 8 + MaxTexCoords = 8 +) + +type Mesh struct { + + //Bitwise combination of PrimitiveType enum + PrimitiveTypes PrimitiveType + Vertices []gglm.Vec3 + Normals []gglm.Vec3 + Tangents []gglm.Vec3 + BitTangents []gglm.Vec3 + + //ColorSets vertex color sets where each set is either empty or has length=len(Vertices), with max number of sets=MaxColorSets + ColorSets [MaxColorSets][]gglm.Vec4 + + //TexCoords (aka UV channels) where each TexCoords[i] has NumUVComponents[i] channels, and is either empty or has length=len(Vertices), with max number of TexCoords per vertex = MaxTexCoords + TexCoords [MaxTexCoords][]gglm.Vec3 + TexCoordChannelCount [MaxTexCoords]uint + + Faces []Face + Bones []*Bone + AnimMeshes []*AnimMesh + AABB AABB + MorphMethod MorphMethod + + MaterialIndex uint + Name string +} + +type Face struct { + Indices []uint +} + +type AnimMesh struct { + Name string + + /** Replacement for Mes.Vertices. If this array is non-NULL, + * it *must* contain mNumVertices entries. The corresponding + * array in the host mesh must be non-NULL as well - animation + * meshes may neither add or nor remove vertex components (if + * a replacement array is NULL and the corresponding source + * array is not, the source data is taken instead)*/ + Vertices []gglm.Vec3 + Normals []gglm.Vec3 + Tangents []gglm.Vec3 + BitTangents []gglm.Vec3 + Colors [MaxColorSets][]gglm.Vec4 + TexCoords [MaxTexCoords][]gglm.Vec3 + + Weight float32 +} + +type AABB struct { + Min gglm.Vec3 + Max gglm.Vec3 +} + +type Bone struct { + Name string + //The influence weights of this bone + Weights []VertexWeight + + /** Matrix that transforms from bone space to mesh space in bind pose. + * + * This matrix describes the position of the mesh + * in the local space of this bone when the skeleton was bound. + * Thus it can be used directly to determine a desired vertex position, + * given the world-space transform of the bone when animated, + * and the position of the vertex in mesh space. + * + * It is sometimes called an inverse-bind matrix, + * or inverse bind pose matrix. + */ + OffsetMatrix gglm.Mat4 +} + +type VertexWeight struct { + VertIndex uint + //The strength of the influence in the range (0...1). The total influence from all bones at one vertex is 1 + Weight float32 +} diff --git a/go.mod b/go.mod index a04a7ff..d5ae879 100755 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module github.com/bloeys/assimp-go go 1.17 + +require github.com/bloeys/gglm v0.2.6 // indirect diff --git a/go.sum b/go.sum new file mode 100755 index 0000000..9d5060f --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/bloeys/gglm v0.2.6 h1:+6m+GZuabU9GRhtEfqz7NS3fewO1xMcjJEenKVPRosM= +github.com/bloeys/gglm v0.2.6/go.mod h1:qwJQ0WzV191wAMwlGicbfbChbKoSedMk7gFFX6GnyOk= diff --git a/main.go b/main.go index 04221d4..7b7701e 100755 --- a/main.go +++ b/main.go @@ -1,14 +1,22 @@ package main import ( + "fmt" + "github.com/bloeys/assimp-go/asig" ) func main() { scene := asig.ImportFile("obj.obj", 0) - println(scene) - // meshes := scene.MMeshes() + + for i := 0; i < len(scene.Meshes); i++ { + + println("Mesh:", i, "; Verts:", len(scene.Meshes[i].Vertices), "; Normals:", len(scene.Meshes[i].Normals)) + for j := 0; j < len(scene.Meshes[i].Vertices); j++ { + fmt.Printf("V(%v): (%v, %v, %v)\n", j, scene.Meshes[i].Vertices[j].X(), scene.Meshes[i].Vertices[j].Y(), scene.Meshes[i].Vertices[j].Z()) + } + } // verts := meshes.Get(0).MVertices() // for i := 0; i < int(verts.Size()); i++ {