mirror of
https://github.com/bloeys/assimp-go.git
synced 2025-12-29 08:28:20 +00:00
Handle (embedded)textures
This commit is contained in:
93
asig/asig.go
93
asig/asig.go
@ -32,7 +32,9 @@ type Texel struct {
|
|||||||
R, G, B, A byte
|
R, G, B, A byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type Texture struct {
|
type EmbeddedTexture struct {
|
||||||
|
cTex *C.struct_aiTexture
|
||||||
|
|
||||||
/** Width of the texture, in pixels
|
/** Width of the texture, in pixels
|
||||||
*
|
*
|
||||||
* If mHeight is zero the texture is compressed in a format
|
* If mHeight is zero the texture is compressed in a format
|
||||||
@ -70,16 +72,12 @@ type Texture struct {
|
|||||||
FormatHint string
|
FormatHint string
|
||||||
|
|
||||||
/** Data of the texture.
|
/** Data of the texture.
|
||||||
*
|
* Points to an array of mWidth * mHeight aiTexel's (or just len=Width if Height=0, which happens when data is compressed).
|
||||||
* Points to an array of mWidth * mHeight aiTexel's.
|
* The format of the texture data is always RGBA8888.
|
||||||
* The format of the texture data is always ARGB8888 to
|
|
||||||
* make the implementation for user of the library as easy
|
|
||||||
* as possible. If mHeight = 0 this is a pointer to a memory
|
|
||||||
* buffer of size mWidth containing the compressed texture
|
|
||||||
* data. Good luck, have fun!
|
|
||||||
*/
|
*/
|
||||||
Texels []Texel
|
Data []byte
|
||||||
|
|
||||||
|
IsCompressed bool
|
||||||
Filename string
|
Filename string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,10 +97,25 @@ type Scene struct {
|
|||||||
RootNode *Node
|
RootNode *Node
|
||||||
Meshes []*Mesh
|
Meshes []*Mesh
|
||||||
Materials []*Material
|
Materials []*Material
|
||||||
Animations []*Animation
|
|
||||||
Textures []*Texture
|
/** Helper structure to describe an embedded texture
|
||||||
Lights []*Light
|
*
|
||||||
Cameras []*Camera
|
* Normally textures are contained in external files but some file formats embed
|
||||||
|
* them directly in the model file. There are two types of embedded textures:
|
||||||
|
* 1. Uncompressed textures. The color data is given in an uncompressed format.
|
||||||
|
* 2. Compressed textures stored in a file format like png or jpg. The raw file
|
||||||
|
* bytes are given so the application must utilize an image decoder (e.g. DevIL) to
|
||||||
|
* get access to the actual color data.
|
||||||
|
*
|
||||||
|
* Embedded textures are referenced from materials using strings like "*0", "*1", etc.
|
||||||
|
* as the texture paths (a single asterisk character followed by the
|
||||||
|
* zero-based index of the texture in the aiScene::mTextures array).
|
||||||
|
*/
|
||||||
|
Textures []*EmbeddedTexture
|
||||||
|
|
||||||
|
// Animations []*Animation
|
||||||
|
// Lights []*Light
|
||||||
|
// Cameras []*Camera
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scene) releaseCResources() {
|
func (s *Scene) releaseCResources() {
|
||||||
@ -141,10 +154,64 @@ func parseScene(cs *C.struct_aiScene) *Scene {
|
|||||||
s.Flags = SceneFlag(cs.mFlags)
|
s.Flags = SceneFlag(cs.mFlags)
|
||||||
s.Meshes = parseMeshes(cs.mMeshes, uint(cs.mNumMeshes))
|
s.Meshes = parseMeshes(cs.mMeshes, uint(cs.mNumMeshes))
|
||||||
s.Materials = parseMaterials(cs.mMaterials, uint(cs.mNumMaterials))
|
s.Materials = parseMaterials(cs.mMaterials, uint(cs.mNumMaterials))
|
||||||
|
s.Textures = parseTextures(cs.mTextures, uint(s.cScene.mNumTextures))
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseTextures(cTexIn **C.struct_aiTexture, count uint) []*EmbeddedTexture {
|
||||||
|
|
||||||
|
if cTexIn == nil {
|
||||||
|
return []*EmbeddedTexture{}
|
||||||
|
}
|
||||||
|
|
||||||
|
textures := make([]*EmbeddedTexture, count)
|
||||||
|
cTex := unsafe.Slice(cTexIn, count)
|
||||||
|
|
||||||
|
for i := 0; i < int(count); i++ {
|
||||||
|
|
||||||
|
textures[i] = &EmbeddedTexture{
|
||||||
|
cTex: cTex[i],
|
||||||
|
Width: uint(cTex[i].mWidth),
|
||||||
|
Height: uint(cTex[i].mHeight),
|
||||||
|
FormatHint: C.GoString(&cTex[i].achFormatHint[0]),
|
||||||
|
Filename: parseAiString(cTex[i].mFilename),
|
||||||
|
Data: parseTexels(cTex[i].pcData, uint(cTex[i].mWidth), uint(cTex[i].mHeight)),
|
||||||
|
IsCompressed: cTex[i].mHeight == 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return textures
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTexels(cTexelsIn *C.struct_aiTexel, width, height uint) []byte {
|
||||||
|
|
||||||
|
//e.g. like a png. Otherwise we have pure color data
|
||||||
|
isCompressed := height == 0
|
||||||
|
|
||||||
|
texelCount := width
|
||||||
|
if !isCompressed {
|
||||||
|
texelCount *= height
|
||||||
|
}
|
||||||
|
texelCount /= 4
|
||||||
|
|
||||||
|
data := make([]byte, texelCount*4)
|
||||||
|
cTexels := unsafe.Slice(cTexelsIn, texelCount)
|
||||||
|
|
||||||
|
for i := 0; i < int(texelCount); i++ {
|
||||||
|
|
||||||
|
//Order here is important as in a compressed format the order will represent arbitrary bytes, not colors.
|
||||||
|
//In aiTexel the struct field order is {b,g,r,a}, which puts A in the high bits and leads to a format of ARGB8888, therefore it must be maintained here
|
||||||
|
index := i * 4
|
||||||
|
data[index] = byte(cTexels[i].b)
|
||||||
|
data[index+1] = byte(cTexels[i].g)
|
||||||
|
data[index+2] = byte(cTexels[i].r)
|
||||||
|
data[index+3] = byte(cTexels[i].a)
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
func parseMeshes(cm **C.struct_aiMesh, count uint) []*Mesh {
|
func parseMeshes(cm **C.struct_aiMesh, count uint) []*Mesh {
|
||||||
|
|
||||||
if cm == nil {
|
if cm == nil {
|
||||||
|
|||||||
@ -1,5 +1,18 @@
|
|||||||
package asig
|
package asig
|
||||||
|
|
||||||
|
type aiReturn int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
//Indicates that a function was successful
|
||||||
|
aiReturnSuccess = 0x0
|
||||||
|
|
||||||
|
//Indicates that a function failed
|
||||||
|
aiReturnFailure = -0x1
|
||||||
|
|
||||||
|
//Indicates that not enough memory was available to perform the requested operation
|
||||||
|
aiReturnOutofMemory = -0x3
|
||||||
|
)
|
||||||
|
|
||||||
type SceneFlag int32
|
type SceneFlag int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@ -24,6 +24,10 @@ enum aiReturn aiGetMaterialTexture(
|
|||||||
unsigned int* flags);
|
unsigned int* flags);
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
type Material struct {
|
type Material struct {
|
||||||
cMat *C.struct_aiMaterial
|
cMat *C.struct_aiMaterial
|
||||||
@ -59,12 +63,35 @@ type MaterialProperty struct {
|
|||||||
*/
|
*/
|
||||||
TypeInfo MatPropertyTypeInfo
|
TypeInfo MatPropertyTypeInfo
|
||||||
|
|
||||||
/** Binary buffer to hold the property's value.
|
//Binary buffer to hold the property's value.
|
||||||
* The size of the buffer is always mDataLength.
|
|
||||||
*/
|
|
||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMaterialTextureCount(m *Material, texType TextureType) int {
|
func GetMaterialTextureCount(m *Material, texType TextureType) int {
|
||||||
return int(C.aiGetMaterialTextureCount(m.cMat, uint32(texType)))
|
return int(C.aiGetMaterialTextureCount(m.cMat, uint32(texType)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetMatTexInfo struct {
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMaterialTexture(m *Material, texType TextureType, texIndex uint) (*GetMatTexInfo, error) {
|
||||||
|
|
||||||
|
outCPath := &C.struct_aiString{}
|
||||||
|
status := aiReturn(C.aiGetMaterialTexture(m.cMat, uint32(texType), C.uint(texIndex), outCPath, nil, nil, nil, nil, nil, nil))
|
||||||
|
if status == aiReturnSuccess {
|
||||||
|
return &GetMatTexInfo{
|
||||||
|
Path: parseAiString(*outCPath),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if status == aiReturnFailure {
|
||||||
|
return nil, errors.New("get texture failed: " + getAiErr().Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if status == aiReturnOutofMemory {
|
||||||
|
return nil, errors.New("get texture failed: out of memory")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("get texture failed: unknown error with code " + fmt.Sprintf("%v", status))
|
||||||
|
}
|
||||||
|
|||||||
2
go.mod
2
go.mod
@ -2,4 +2,4 @@ module github.com/bloeys/assimp-go
|
|||||||
|
|
||||||
go 1.17
|
go 1.17
|
||||||
|
|
||||||
require github.com/bloeys/gglm v0.2.6 // indirect
|
require github.com/bloeys/gglm v0.2.6
|
||||||
|
|||||||
41
main.go
41
main.go
@ -1,14 +1,16 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"image/png"
|
||||||
|
|
||||||
"github.com/bloeys/assimp-go/asig"
|
"github.com/bloeys/assimp-go/asig"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
scene, release, err := asig.ImportFile("tex-cube.fbx", asig.PostProcessTriangulate)
|
scene, release, err := asig.ImportFile("tex-cube.glb", asig.PostProcessTriangulate)
|
||||||
defer release()
|
defer release()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -17,7 +19,7 @@ func main() {
|
|||||||
|
|
||||||
for i := 0; i < len(scene.Meshes); i++ {
|
for i := 0; i < len(scene.Meshes); i++ {
|
||||||
|
|
||||||
println("Mesh:", i, "; Verts:", len(scene.Meshes[i].Vertices), "; Normals:", len(scene.Meshes[i].Normals))
|
println("Mesh:", i, "; Verts:", len(scene.Meshes[i].Vertices), "; Normals:", len(scene.Meshes[i].Normals), "; MatIndex:", scene.Meshes[i].MaterialIndex)
|
||||||
for j := 0; j < len(scene.Meshes[i].Vertices); j++ {
|
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())
|
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())
|
||||||
}
|
}
|
||||||
@ -25,13 +27,40 @@ func main() {
|
|||||||
|
|
||||||
for i := 0; i < len(scene.Materials); i++ {
|
for i := 0; i < len(scene.Materials); i++ {
|
||||||
|
|
||||||
|
m := scene.Materials[i]
|
||||||
println("Mesh:", i, "; Props:", len(scene.Materials[i].Properties))
|
println("Mesh:", i, "; Props:", len(scene.Materials[i].Properties))
|
||||||
for j := 0; j < len(scene.Materials[i].Properties); j++ {
|
texCount := asig.GetMaterialTextureCount(m, asig.TextureTypeDiffuse)
|
||||||
|
fmt.Println("Texture count:", texCount)
|
||||||
|
|
||||||
p := scene.Materials[i].Properties[j]
|
if texCount > 0 {
|
||||||
fmt.Printf("Data Type: %v; Len Bytes: %v; Texture Type: %v\n", p.TypeInfo.String(), len(p.Data), p.Semantic.String())
|
|
||||||
|
|
||||||
fmt.Println("Texture count:", asig.GetMaterialTextureCount(scene.Materials[i], asig.TextureTypeDiffuse))
|
texInfo, err := asig.GetMaterialTexture(m, asig.TextureTypeDiffuse, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%v", texInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ts := scene.Textures
|
||||||
|
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))
|
||||||
|
|
||||||
|
if t.FormatHint == "png" {
|
||||||
|
decodePNG(t.Data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decodePNG(texels []byte) {
|
||||||
|
|
||||||
|
img, err := png.Decode(bytes.NewReader(texels))
|
||||||
|
if err != nil {
|
||||||
|
panic("wow2: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
println("C:", img.At(100, 100))
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user