Repository: qmuntal/gltf (go)

Issue:
Can I write `inverseBindMatrices` with `modeler`?
It is `[][16]float32`.

Pull request description:
Implement inverse bind matrices reader and writer
Fixes #91.

Patch:
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/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:


Test patch:
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_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) {