Handle (embedded)textures

This commit is contained in:
bloeys
2021-11-19 21:05:21 +04:00
parent 3b31e8d677
commit 4462a3be04
5 changed files with 163 additions and 27 deletions

View File

@ -32,7 +32,9 @@ type Texel struct {
R, G, B, A byte
}
type Texture struct {
type EmbeddedTexture struct {
cTex *C.struct_aiTexture
/** Width of the texture, in pixels
*
* If mHeight is zero the texture is compressed in a format
@ -70,16 +72,12 @@ type Texture struct {
FormatHint string
/** Data of the texture.
*
* Points to an array of mWidth * mHeight aiTexel's.
* 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!
* Points to an array of mWidth * mHeight aiTexel's (or just len=Width if Height=0, which happens when data is compressed).
* The format of the texture data is always RGBA8888.
*/
Texels []Texel
Data []byte
IsCompressed bool
Filename string
}
@ -99,10 +97,25 @@ type Scene struct {
RootNode *Node
Meshes []*Mesh
Materials []*Material
Animations []*Animation
Textures []*Texture
Lights []*Light
Cameras []*Camera
/** Helper structure to describe an embedded texture
*
* 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() {
@ -141,10 +154,64 @@ func parseScene(cs *C.struct_aiScene) *Scene {
s.Flags = SceneFlag(cs.mFlags)
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))
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 {
if cm == nil {

View File

@ -1,5 +1,18 @@
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
const (

View File

@ -24,6 +24,10 @@ enum aiReturn aiGetMaterialTexture(
unsigned int* flags);
*/
import "C"
import (
"errors"
"fmt"
)
type Material struct {
cMat *C.struct_aiMaterial
@ -59,12 +63,35 @@ type MaterialProperty struct {
*/
TypeInfo MatPropertyTypeInfo
/** Binary buffer to hold the property's value.
* The size of the buffer is always mDataLength.
*/
//Binary buffer to hold the property's value.
Data []byte
}
func GetMaterialTextureCount(m *Material, texType TextureType) int {
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
View File

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

41
main.go
View File

@ -1,14 +1,16 @@
package main
import (
"bytes"
"fmt"
"image/png"
"github.com/bloeys/assimp-go/asig"
)
func main() {
scene, release, err := asig.ImportFile("tex-cube.fbx", asig.PostProcessTriangulate)
scene, release, err := asig.ImportFile("tex-cube.glb", asig.PostProcessTriangulate)
defer release()
if err != nil {
@ -17,7 +19,7 @@ func main() {
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++ {
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++ {
m := scene.Materials[i]
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]
fmt.Printf("Data Type: %v; Len Bytes: %v; Texture Type: %v\n", p.TypeInfo.String(), len(p.Data), p.Semantic.String())
if texCount > 0 {
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))
}