4 Commits

Author SHA1 Message Date
d583049090 Update to 5.1.6+use dll+update readme 2022-01-22 03:39:12 +04:00
9acd5bee18 Metadata+Fix node bug 2021-11-20 01:24:37 +04:00
5b5d091d56 Nodes implementation 2021-11-20 00:37:59 +04:00
c0a308b178 Add usage and features to readme 2021-11-20 00:12:07 +04:00
7 changed files with 225 additions and 33 deletions

View File

@ -1,7 +1,80 @@
# assimp-go
A Handcrafted Open Asset Import Library (AssImp) wrapper for Go.
## Features
The following features are already implemented:
* Loading all supported model formats into a Scene object
* Mesh data
* Materials
* Textures and embedded textures
* Error reporting
* Enums relevant to the above operations
Unimplemented (yet) AssImp Scene objects:
* Animation
* Lights
* Camera
## Using assimp-go
### Requirements
To run the project you need:
* A recent version of [Go](https://golang.org/) installed (1.17+)
* A C/C++ compiler installed and in your path
* Windows: [MingW](https://www.mingw-w64.org/downloads/#mingw-builds) or similar
* Mac/Linux: Should be installed by default, but if not try [GCC](https://gcc.gnu.org/) or [Clang](https://releases.llvm.org/download.html)
Then simply clone and use `go run .`
> Note: that it might take a while to run the first time because of downloading/compiling dependencies.
`assimp-go` dynamically links (e.g. through a DLL) AssImp using platform dependent libraries, which are made available with the GitHub releases.
Currently only `Windows` libraries are available, but more should be easy to add by compiling AssImp on the wanted platform. (Make a PR if you can help us get those binaries!)
### Getting Started
```Go
func main() {
//Load this .fbx model with the following post processing flags
scene, release, err := asig.ImportFile("my-cube.fbx", asig.PostProcessTriangulate | asig.PostProcessJoinIdenticalVertices)
if err != nil {
panic(err)
}
for i := 0; i < len(scene.Materials); i++ {
m := scene.Materials[i]
//Check how many diffuse textures are attached to this material
texCount := asig.GetMaterialTextureCount(m, asig.TextureTypeDiffuse)
fmt.Println("Texture count:", texCount)
//If we have at least 1 diffuse texture attached to this material, load the first diffuse texture (index 0)
if texCount > 0 {
texInfo, err := asig.GetMaterialTexture(m, asig.TextureTypeDiffuse, 0)
if err != nil {
panic(err)
}
fmt.Printf("%v\n", texInfo)
}
}
//Now that we are done with all our `asig.XYZ` calls we can release underlying C resources.
//
//NOTE: Our Go objects (like scene, scene.Materials etc) will remain intact ;), but we must NOT use asig.XYZ calls on this scene and its children anymore
release()
}
```
The `release()` function is used to free underlying C resources and should be called after all processing that requires C code is done.
`release()` Will not affect the returned Go structs like `Scene` or `Mesh`. Returned Go data will remain valid.
@ -11,30 +84,28 @@ While `asig` functions should NOT be called on a Scene (or its objects) after th
## Developing assimp-go
We link against static assimp libraries that are built for each platform and added to the `asig/libs` package.
We link against assimp libraries that are built for each platform and the `*.a` files are added to the `asig/libs` package.
Depending on the platform we select one of them and link against it when doing `go build`.
The general steps are:
- Copy assimp includes into `asig/assimp`
- Copy `zlib.h`, `zconf.h` and `irrXML.h` into `asig/zlib` and `asig/irrxml` respectively.
- Copy static libraries into `asig/libs`
- Generate the wrappers using `swig -go -c++ -intgosize 64 asig/asig.i`
- Add `#cgo LDFLAGS: -L ./staticLibs -l zlibstatic -l IrrXML -l assimp` at the top of the 'C' import in `asig.go`
* Copy assimp includes into `asig/assimp`
* Copy `zlib.h`, `zconf.h` and `irrXML.h` into `asig/zlib` and `asig/irrxml` respectively.
* Copy static libraries and DLL import libraries into `asig/libs`
> Note: When dealing with static libraries the compiler will probably (e.g. MinGW does this) ignore `lib` suffixes and `.a`/`.lib` suffixes.
> Note: When dealing with libraries the compiler will probably (e.g. MinGW does this) ignore `lib` prefixes and `.a`/`.lib` suffixes.
So if your lib name is `libassimp.a` you need to pass it to CGO as `-l assimp`, otherwise you will get an error about library not found.
For platform specific steps:
**Windows**:
> Note: You must compile with the same C/C++ compiler you use with Go (e.g. if you use MinGW with Go, then compile assimp with MinGW by sepcifying the correct `-G` option)
> Note: You must compile with the same C/C++ compiler you use with Go (e.g. if you use MinGW with Go, then compile assimp with MinGW by specifying the correct `-G` option to cMake)
---
> Note: If you get compilation errors with things like `File too big` or `can't write 166 bytes to section` then cmake isn't detecting you are using MinGW, so add this flag `-D CMAKE_COMPILER_IS_MINGW=TRUE`
Now assuming you are using MinGW on windows:
- Clone wanted release of assimp and run `cmake CMakeLists.txt -D BUILD_SHARED_LIBS=OFF -D ASSIMP_BUILD_ZLIB=ON -D ASSIMP_BUILD_ASSIMP_TOOLS=OFF -D ASSIMP_BUILD_TESTS=OFF -G "MinGW Makefiles"` in the root folder
- Run `cmake --build . --parallel 6`
- Copy the generated `*.lib` (or `*.a`) files into `asig/lib`
* Clone wanted release of assimp and run `cmake CMakeLists.txt -D ASSIMP_BUILD_ASSIMP_TOOLS=OFF -G "MinGW Makefiles"` in the root folder
* Run `cmake --build . --parallel 6`
* Copy the generated `*.lib` (or `*.a`) files from the `lib` folder and into `asig/lib`, and copy the generated dll from AssImp `bin` folder into the root of `assimp-go`.

View File

@ -4,15 +4,8 @@ package asig
#cgo CFLAGS: -I .
#cgo LDFLAGS: -L ./libs -l assimp_windows_amd64 -l IrrXML_windows_amd64 -l zlib_windows_amd64
#include <wrap.cxx>
#include <stdlib.h> //Needed for C.free
#include <assimp/scene.h>
//Functions
struct aiScene* aiImportFile(const char* pFile, unsigned int pFlags);
void aiReleaseImport(const struct aiScene* pScene);
const char* aiGetErrorString();
unsigned int aiGetMaterialTextureCount(const struct aiMaterial* pMat, enum aiTextureType type);
*/
import "C"
import (
@ -23,15 +16,31 @@ import (
)
type Node struct {
Name string
//The transformation relative to the node's parent
Transformation *gglm.Mat4
//Parent node. NULL if this node is the root node
Parent *Node
//The child nodes of this node. NULL if mNumChildren is 0
Children []*Node
//Each entry is an index into the mesh list of the scene
MeshIndicies []uint
/** Metadata associated with this node or NULL if there is no metadata.
* Whether any metadata is generated depends on the source file format. See the
* @link importer_notes @endlink page for more information on every source file
* format. Importers that don't document any metadata don't write any.
*/
Metadata map[string]Metadata
}
type Animation struct {
}
type Texel struct {
R, G, B, A byte
}
type EmbeddedTexture struct {
cTex *C.struct_aiTexture
@ -88,6 +97,12 @@ type Camera struct {
}
type Metadata struct {
Type MetadataType
Value interface{}
}
type MetadataEntry struct {
Data []byte
}
type Scene struct {
@ -152,6 +167,7 @@ func parseScene(cs *C.struct_aiScene) *Scene {
s := &Scene{cScene: cs}
s.Flags = SceneFlag(cs.mFlags)
s.RootNode = parseRootNode(cs.mRootNode)
s.Meshes = parseMeshes(cs.mMeshes, uint(cs.mNumMeshes))
s.Materials = parseMaterials(cs.mMaterials, uint(cs.mNumMaterials))
s.Textures = parseTextures(cs.mTextures, uint(s.cScene.mNumTextures))
@ -159,6 +175,95 @@ func parseScene(cs *C.struct_aiScene) *Scene {
return s
}
func parseRootNode(cNodesIn *C.struct_aiNode) *Node {
rn := &Node{
Name: parseAiString(cNodesIn.mName),
Transformation: parseMat4(&cNodesIn.mTransformation),
Parent: nil,
MeshIndicies: parseUInts(cNodesIn.mMeshes, uint(cNodesIn.mNumMeshes)),
Metadata: parseMetadata(cNodesIn.mMetaData),
}
rn.Children = parseNodes(cNodesIn.mChildren, rn, uint(cNodesIn.mNumChildren))
return rn
}
func parseNodes(cNodesIn **C.struct_aiNode, parent *Node, parentChildrenCount uint) []*Node {
if cNodesIn == nil {
return []*Node{}
}
nodes := make([]*Node, parentChildrenCount)
cNodes := unsafe.Slice(cNodesIn, parentChildrenCount)
for i := 0; i < len(nodes); i++ {
n := cNodes[i]
//Fill basic node info
nodes[i] = &Node{
Name: parseAiString(n.mName),
Transformation: parseMat4(&n.mTransformation),
Parent: parent,
MeshIndicies: parseUInts(n.mMeshes, uint(n.mNumMeshes)),
Metadata: parseMetadata(n.mMetaData),
}
//Parse node's children
nodes[i].Children = parseNodes(n.mChildren, nodes[i], parentChildrenCount)
}
return nodes
}
func parseMetadata(cMetaIn *C.struct_aiMetadata) map[string]Metadata {
if cMetaIn == nil {
return map[string]Metadata{}
}
meta := make(map[string]Metadata, cMetaIn.mNumProperties)
cKeys := unsafe.Slice(cMetaIn.mKeys, cMetaIn.mNumProperties)
cVals := unsafe.Slice(cMetaIn.mValues, cMetaIn.mNumProperties)
for i := 0; i < int(cMetaIn.mNumProperties); i++ {
meta[parseAiString(cKeys[i])] = parseMetadataEntry(cVals[i])
}
return meta
}
func parseMetadataEntry(cv C.struct_aiMetadataEntry) Metadata {
m := Metadata{Type: MetadataType(cv.mType)}
if cv.mData == nil {
return m
}
switch m.Type {
case MetadataTypeBool:
m.Value = *(*bool)(cv.mData)
case MetadataTypeFloat32:
m.Value = *(*float32)(cv.mData)
case MetadataTypeFloat64:
m.Value = *(*float64)(cv.mData)
case MetadataTypeInt32:
m.Value = *(*int32)(cv.mData)
case MetadataTypeUint64:
m.Value = *(*uint64)(cv.mData)
case MetadataTypeString:
m.Value = parseAiString(*(*C.struct_aiString)(cv.mData))
case MetadataTypeVec3:
m.Value = parseVec3((*C.struct_aiVector3D)(cv.mData))
}
return m
}
func parseTextures(cTexIn **C.struct_aiTexture, count uint) []*EmbeddedTexture {
if cTexIn == nil {
@ -367,20 +472,20 @@ func parseBones(cbs **C.struct_aiBone, count uint) []*Bone {
bones[i] = &Bone{
Name: parseAiString(cBone.mName),
Weights: parseVertexWeights(cBone.mWeights, uint(cBone.mNumWeights)),
OffsetMatrix: parseMat4(&cBone.mOffsetMatrix),
OffsetMatrix: *parseMat4(&cBone.mOffsetMatrix),
}
}
return bones
}
func parseMat4(cm4 *C.struct_aiMatrix4x4) gglm.Mat4 {
func parseMat4(cm4 *C.struct_aiMatrix4x4) *gglm.Mat4 {
if cm4 == nil {
return gglm.Mat4{}
return &gglm.Mat4{}
}
return 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)},

View File

@ -315,3 +315,16 @@ func (mpti MatPropertyTypeInfo) String() string {
return "Unknown"
}
}
type MetadataType int32
const (
MetadataTypeBool MetadataType = 0
MetadataTypeInt32 MetadataType = 1
MetadataTypeUint64 MetadataType = 2
MetadataTypeFloat32 MetadataType = 3
MetadataTypeFloat64 MetadataType = 4
MetadataTypeString MetadataType = 5
MetadataTypeVec3 MetadataType = 6
MetadataTypeMAX MetadataType = 7
)

Binary file not shown.

2
go.mod
View File

@ -2,4 +2,4 @@ module github.com/bloeys/assimp-go
go 1.17
require github.com/bloeys/gglm v0.2.6
require github.com/bloeys/gglm v0.3.1

2
go.sum
View File

@ -1,2 +1,4 @@
github.com/bloeys/gglm v0.2.6 h1:+6m+GZuabU9GRhtEfqz7NS3fewO1xMcjJEenKVPRosM=
github.com/bloeys/gglm v0.2.6/go.mod h1:qwJQ0WzV191wAMwlGicbfbChbKoSedMk7gFFX6GnyOk=
github.com/bloeys/gglm v0.3.1 h1:Sy9upW7SBsBfDXrSmEhid3aQ+7J7itej+upwcxOnPMQ=
github.com/bloeys/gglm v0.3.1/go.mod h1:qwJQ0WzV191wAMwlGicbfbChbKoSedMk7gFFX6GnyOk=

11
main.go
View File

@ -10,12 +10,13 @@ import (
func main() {
scene, release, err := asig.ImportFile("tex-cube.glb", asig.PostProcessTriangulate)
defer release()
scene, release, err := asig.ImportFile("obj.obj", asig.PostProcessTriangulate|asig.PostProcessJoinIdenticalVertices)
if err != nil {
panic(err)
}
defer release()
fmt.Printf("RootNode: %+v\n\n", scene.RootNode)
for i := 0; i < len(scene.Meshes); i++ {
@ -28,7 +29,7 @@ func main() {
for i := 0; i < len(scene.Materials); i++ {
m := scene.Materials[i]
println("Mesh:", i, "; Props:", len(scene.Materials[i].Properties))
println("Material:", i, "; Props:", len(scene.Materials[i].Properties))
texCount := asig.GetMaterialTextureCount(m, asig.TextureTypeDiffuse)
fmt.Println("Texture count:", texCount)
@ -47,7 +48,7 @@ func main() {
for i := 0; i < len(ts); i++ {
t := ts[i]
fmt.Printf("T(%v): Name=%v, Hint=%v, Width=%v, Height=%v, NumTexels=%v", i, t.Filename, t.FormatHint, t.Width, t.Height, len(t.Data))
fmt.Printf("T(%v): Name=%v, Hint=%v, Width=%v, Height=%v, NumTexels=%v\n", i, t.Filename, t.FormatHint, t.Width, t.Height, len(t.Data))
if t.FormatHint == "png" {
decodePNG(t.Data)