diff --git a/modeler/read.go b/modeler/read.go index f085cab..acba2fb 100644 --- a/modeler/read.go +++ b/modeler/read.go @@ -453,6 +453,27 @@ func ReadColor64(doc *gltf.Document, acr *gltf.Accessor, buffer [][4]uint16) ([] return buffer, nil } +// ReadInverseBindMatrices returns the data referenced by acr. +// +// See ReadAccessor for more info. +func ReadInverseBindMatrices(doc *gltf.Document, acr *gltf.Accessor, buffer [][4][4]float32) ([][4][4]float32, error) { + if acr.ComponentType != gltf.ComponentFloat { + return nil, errComponentType(acr.ComponentType) + } + if acr.Type != gltf.AccessorMat4 { + return nil, errAccessorType(acr.Type) + } + bufPtr := bufPool.Get().(*[]byte) + defer bufPool.Put(bufPtr) + data, err := ReadAccessor(doc, acr, *bufPtr) + if err != nil { + return nil, err + } + buffer = makeBufferOf(acr.Count, buffer) + copy(buffer, data.([][4][4]float32)) + return buffer, nil +} + func errAccessorType(tp gltf.AccessorType) error { return fmt.Errorf("gltf: accessor type %v not allowed", tp) } diff --git a/modeler/read_test.go b/modeler/read_test.go index 13229bb..b602cd0 100644 --- a/modeler/read_test.go +++ b/modeler/read_test.go @@ -694,3 +694,53 @@ func TestReadColor64(t *testing.T) { }) } } + +func TestReadInverseBindMatrices(t *testing.T) { + type args struct { + data []byte + acr *gltf.Accessor + buffer [][4][4]float32 + } + tests := []struct { + name string + args args + want [][4][4]float32 + wantErr bool + }{ + {"base", args{[]byte{ + 0, 0, 128, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 128, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, &gltf.Accessor{BufferView: gltf.Index(0), Count: 1, Type: gltf.AccessorMat4, ComponentType: gltf.ComponentFloat}, nil}, + [][4][4]float32{{{1, 2, 3, 4}}}, false, + }, + {"incorrect-type", args{[]byte{}, &gltf.Accessor{ + BufferView: gltf.Index(0), Type: gltf.AccessorMat2, ComponentType: gltf.ComponentFloat, + }, nil}, nil, true}, + {"incorrect-componenttype", args{[]byte{}, &gltf.Accessor{ + BufferView: gltf.Index(0), Type: gltf.AccessorMat4, ComponentType: gltf.ComponentByte, + }, nil}, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + doc := &gltf.Document{ + BufferViews: []*gltf.BufferView{ + {Buffer: 0, ByteLength: len(tt.args.data)}, + }, + Buffers: []*gltf.Buffer{ + {Data: tt.args.data, ByteLength: len(tt.args.data)}, + }, + } + got, err := modeler.ReadInverseBindMatrices(doc, tt.args.acr, tt.args.buffer) + if (err != nil) != tt.wantErr { + t.Errorf("ReadInverseBindMatrices() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ReadInverseBindMatrices() = %v, want %v", got, tt.want) + } + }) + } + +} diff --git a/modeler/write.go b/modeler/write.go index 9bb541e..bbcfd6e 100644 --- a/modeler/write.go +++ b/modeler/write.go @@ -102,6 +102,13 @@ func WriteJoints(doc *gltf.Document, data any) int { return WriteAccessor(doc, gltf.TargetArrayBuffer, data) } +// WriteInverseBindMatrices adds a new inverse bind matrices accessor to doc +// and fills the last buffer with data. +// If success it returns the index of the new accessor. +func WriteInverseBindMatrices(doc *gltf.Document, data [][4][4]float32) int { + return WriteAccessor(doc, gltf.TargetArrayBuffer, data) +} + func checkJoints(data any) error { switch data.(type) { case [][4]uint8, [][4]uint16: diff --git a/modeler/write_test.go b/modeler/write_test.go index 904fdcb..36e7ae0 100644 --- a/modeler/write_test.go +++ b/modeler/write_test.go @@ -733,6 +733,49 @@ func TestWriteImage(t *testing.T) { } } +func TestWriteInverseBindMatrices(t *testing.T) { + tests := []struct { + name string + m *gltf.Document + args [][4][4]float32 + want int + wantDoc *gltf.Document + }{ + {"base", &gltf.Document{ + Accessors: []*gltf.Accessor{{}}, + }, [][4][4]float32{{{1, 2, 3, 4}}}, 1, &gltf.Document{ + Accessors: []*gltf.Accessor{ + {}, + {BufferView: gltf.Index(0), Count: 1, Type: gltf.AccessorMat4, ComponentType: gltf.ComponentFloat}, + }, + BufferViews: []*gltf.BufferView{ + {ByteLength: 64, Target: gltf.TargetArrayBuffer}, + }, + Buffers: []*gltf.Buffer{ + {ByteLength: 64, Data: []byte{ + 0, 0, 128, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 128, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }}, + }, + }}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := modeler.WriteInverseBindMatrices(tt.m, tt.args) + if tt.want != got { + t.Errorf("WriteInverseBindMatrices() = %v, want %v", got, tt.want) + return + } + if diff := deep.Equal(tt.m, tt.wantDoc); diff != nil { + t.Errorf("WriteInverseBindMatrices() = %v", diff) + return + } + }) + } +} + type errReader struct{} func (r *errReader) Read(p []byte) (int, error) {