mirror of
https://github.com/bloeys/assimp-go.git
synced 2025-12-29 08:28:20 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d583049090 | |||
| 9acd5bee18 | |||
| 5b5d091d56 | |||
| c0a308b178 |
93
README.md
93
README.md
@ -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`.
|
||||
|
||||
137
asig/asig.go
137
asig/asig.go
@ -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)},
|
||||
|
||||
@ -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
2
go.mod
@ -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
2
go.sum
@ -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
11
main.go
@ -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)
|
||||
|
||||
Reference in New Issue
Block a user