diff --git a/binary/slice.go b/binary/slice.go index 5c0bcf1..ecb42d9 100644 --- a/binary/slice.go +++ b/binary/slice.go @@ -4,35 +4,149 @@ import ( "fmt" "image/color" "reflect" + "unsafe" "github.com/qmuntal/gltf" ) +func castSlice(c gltf.ComponentType, t gltf.AccessorType, v []byte) any { + var ptr unsafe.Pointer + if len(v) != 0 { + ptr = unsafe.Pointer(&v[0]) + } + switch c { + case gltf.ComponentUbyte: + switch t { + case gltf.AccessorScalar: + return v + case gltf.AccessorVec2: + return unsafe.Slice((*[2]uint8)(ptr), len(v)/2) + case gltf.AccessorVec3: + return unsafe.Slice((*[3]uint8)(ptr), len(v)/3) + case gltf.AccessorVec4: + return unsafe.Slice((*[4]uint8)(ptr), len(v)/4) + case gltf.AccessorMat2: + return unsafe.Slice((*[2][2]uint8)(ptr), len(v)/4) + case gltf.AccessorMat3: + return unsafe.Slice((*[3][3]uint8)(ptr), len(v)/9) + case gltf.AccessorMat4: + return unsafe.Slice((*[4][4]uint8)(ptr), len(v)/16) + } + case gltf.ComponentByte: + switch t { + case gltf.AccessorScalar: + return unsafe.Slice((*int8)(ptr), len(v)) + case gltf.AccessorVec2: + return unsafe.Slice((*[2]int8)(ptr), len(v)/2) + case gltf.AccessorVec3: + return unsafe.Slice((*[3]int8)(ptr), len(v)/3) + case gltf.AccessorVec4: + return unsafe.Slice((*[4]int8)(ptr), len(v)/4) + case gltf.AccessorMat2: + return unsafe.Slice((*[2][2]int8)(ptr), len(v)/4) + case gltf.AccessorMat3: + return unsafe.Slice((*[3][3]int8)(ptr), len(v)/9) + case gltf.AccessorMat4: + return unsafe.Slice((*[4][4]int8)(ptr), len(v)/16) + } + case gltf.ComponentUshort: + switch t { + case gltf.AccessorScalar: + return unsafe.Slice((*uint16)(ptr), len(v)/2) + case gltf.AccessorVec2: + return unsafe.Slice((*[2]uint16)(ptr), len(v)/4) + case gltf.AccessorVec3: + return unsafe.Slice((*[3]uint16)(ptr), len(v)/6) + case gltf.AccessorVec4: + return unsafe.Slice((*[4]uint16)(ptr), len(v)/8) + case gltf.AccessorMat2: + return unsafe.Slice((*[2][2]uint16)(ptr), len(v)/8) + case gltf.AccessorMat3: + return unsafe.Slice((*[3][3]uint16)(ptr), len(v)/18) + case gltf.AccessorMat4: + return unsafe.Slice((*[4][4]uint16)(ptr), len(v)/32) + } + case gltf.ComponentShort: + switch t { + case gltf.AccessorScalar: + return unsafe.Slice((*int16)(ptr), len(v)/2) + case gltf.AccessorVec2: + return unsafe.Slice((*[2]int16)(ptr), len(v)/4) + case gltf.AccessorVec3: + return unsafe.Slice((*[3]int16)(ptr), len(v)/6) + case gltf.AccessorVec4: + return unsafe.Slice((*[4]int16)(ptr), len(v)/8) + case gltf.AccessorMat2: + return unsafe.Slice((*[2][2]int16)(ptr), len(v)/8) + case gltf.AccessorMat3: + return unsafe.Slice((*[3][3]int16)(ptr), len(v)/18) + case gltf.AccessorMat4: + return unsafe.Slice((*[4][4]int16)(ptr), len(v)/32) + } + case gltf.ComponentUint: + switch t { + case gltf.AccessorScalar: + return unsafe.Slice((*uint32)(ptr), len(v)/4) + case gltf.AccessorVec2: + return unsafe.Slice((*[2]uint32)(ptr), len(v)/8) + case gltf.AccessorVec3: + return unsafe.Slice((*[3]uint32)(ptr), len(v)/12) + case gltf.AccessorVec4: + return unsafe.Slice((*[4]uint32)(ptr), len(v)/16) + case gltf.AccessorMat2: + return unsafe.Slice((*[2][2]uint32)(ptr), len(v)/16) + case gltf.AccessorMat3: + return unsafe.Slice((*[3][3]uint32)(ptr), len(v)/36) + case gltf.AccessorMat4: + return unsafe.Slice((*[4][4]uint32)(ptr), len(v)/64) + } + case gltf.ComponentFloat: + switch t { + case gltf.AccessorScalar: + return unsafe.Slice((*float32)(ptr), len(v)/4) + case gltf.AccessorVec2: + return unsafe.Slice((*[2]float32)(ptr), len(v)/8) + case gltf.AccessorVec3: + return unsafe.Slice((*[3]float32)(ptr), len(v)/12) + case gltf.AccessorVec4: + return unsafe.Slice((*[4]float32)(ptr), len(v)/16) + case gltf.AccessorMat2: + return unsafe.Slice((*[2][2]float32)(ptr), len(v)/16) + case gltf.AccessorMat3: + return unsafe.Slice((*[3][3]float32)(ptr), len(v)/36) + case gltf.AccessorMat4: + return unsafe.Slice((*[4][4]float32)(ptr), len(v)/64) + } + } + return nil +} + // MakeSliceBuffer returns the slice type associated with c and t and with the given element count. // If the buffer is an slice which type matches with the expected by the acr then it will // be used as backing slice. -func MakeSliceBuffer(c gltf.ComponentType, t gltf.AccessorType, count int, buffer any) any { - if buffer == nil { +func MakeSliceBuffer(c gltf.ComponentType, t gltf.AccessorType, count int, buffer []byte) (any, error) { + if len(buffer) == 0 { return MakeSlice(c, t, count) } - c1, t1, count1 := Type(buffer) - if count1 == 0 || c1 != c || t1 != t { + v := castSlice(c, t, buffer) + if v == nil { return MakeSlice(c, t, count) } + count1 := reflect.ValueOf(v).Len() if count1 < count { - tmpSlice := MakeSlice(c, t, count-count1) - return reflect.AppendSlice(reflect.ValueOf(buffer), reflect.ValueOf(tmpSlice)).Interface() + tmpSlice, _ := MakeSlice(c, t, count-count1) + return reflect.AppendSlice(reflect.ValueOf(v), reflect.ValueOf(tmpSlice)).Interface(), nil } if count1 > count { - return reflect.ValueOf(buffer).Slice(0, int(count)).Interface() + return reflect.ValueOf(v).Slice(0, int(count)).Interface(), nil } - return buffer + return v, nil } // MakeSlice returns the slice type associated with c and t and with the given element count. // For example, if c is gltf.ComponentFloat and t is gltf.AccessorVec3 // then MakeSlice(c, t, 5) is equivalent to make([][3]float32, 5). -func MakeSlice(c gltf.ComponentType, t gltf.AccessorType, count int) any { +func MakeSlice(c gltf.ComponentType, t gltf.AccessorType, count int) (any, error) { var tp reflect.Type switch c { case gltf.ComponentUbyte: @@ -47,9 +161,13 @@ func MakeSlice(c gltf.ComponentType, t gltf.AccessorType, count int) any { tp = reflect.TypeOf((*uint32)(nil)) case gltf.ComponentFloat: tp = reflect.TypeOf((*float32)(nil)) + default: + return nil, fmt.Errorf("gltf: unsupported component type %d", c) } tp = tp.Elem() switch t { + case gltf.AccessorScalar: + // Nothing to do. case gltf.AccessorVec2: tp = reflect.ArrayOf(2, tp) case gltf.AccessorVec3: @@ -62,8 +180,10 @@ func MakeSlice(c gltf.ComponentType, t gltf.AccessorType, count int) any { tp = reflect.ArrayOf(3, reflect.ArrayOf(3, tp)) case gltf.AccessorMat4: tp = reflect.ArrayOf(4, reflect.ArrayOf(4, tp)) + default: + return nil, fmt.Errorf("gltf: unsupported accessor type %d", t) } - return reflect.MakeSlice(reflect.SliceOf(tp), count, count).Interface() + return reflect.MakeSlice(reflect.SliceOf(tp), count, count).Interface(), nil } // Type returns the associated glTF type data. diff --git a/binary/slice_test.go b/binary/slice_test.go index fb7edc2..6232076 100644 --- a/binary/slice_test.go +++ b/binary/slice_test.go @@ -71,7 +71,7 @@ func TestMakeSlice(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := binary.MakeSlice(tt.args.c, tt.args.t, tt.args.count); !reflect.DeepEqual(got, tt.want) { + if got, _ := binary.MakeSlice(tt.args.c, tt.args.t, tt.args.count); !reflect.DeepEqual(got, tt.want) { t.Errorf("MakeSlice() = %v, want %v", got, tt.want) } }) @@ -83,7 +83,7 @@ func TestMakeSliceBuffer(t *testing.T) { c gltf.ComponentType t gltf.AccessorType count int - buffer any + buffer []byte } tests := []struct { name string @@ -91,15 +91,15 @@ func TestMakeSliceBuffer(t *testing.T) { want any }{ {"nil buffer", args{gltf.ComponentUbyte, gltf.AccessorVec2, 2, nil}, make([][2]uint8, 2)}, - {"empty buffer", args{gltf.ComponentUbyte, gltf.AccessorVec2, 2, make([][2]uint8, 0)}, make([][2]uint8, 2)}, - {"different buffer", args{gltf.ComponentUbyte, gltf.AccessorVec2, 2, make([][3]int8, 3)}, make([][2]uint8, 2)}, - {"small buffer", args{gltf.ComponentUbyte, gltf.AccessorVec2, 2, make([][2]uint8, 1)}, make([][2]uint8, 2)}, - {"large buffer", args{gltf.ComponentUbyte, gltf.AccessorVec2, 2, make([][2]uint8, 3)}, make([][2]uint8, 2)}, - {"same buffer", args{gltf.ComponentUbyte, gltf.AccessorVec2, 2, make([][2]uint8, 2)}, make([][2]uint8, 2)}, + {"empty buffer", args{gltf.ComponentUbyte, gltf.AccessorVec2, 2, make([]uint8, 0)}, make([][2]uint8, 2)}, + {"different buffer", args{gltf.ComponentUbyte, gltf.AccessorVec2, 2, make([]uint8, 9)}, make([][2]uint8, 2)}, + {"small buffer", args{gltf.ComponentUbyte, gltf.AccessorVec2, 2, make([]uint8, 2)}, make([][2]uint8, 2)}, + {"large buffer", args{gltf.ComponentUbyte, gltf.AccessorVec2, 2, make([]uint8, 6)}, make([][2]uint8, 2)}, + {"same buffer", args{gltf.ComponentUbyte, gltf.AccessorVec2, 2, make([]uint8, 4)}, make([][2]uint8, 2)}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := binary.MakeSliceBuffer(tt.args.c, tt.args.t, tt.args.count, tt.args.buffer); !reflect.DeepEqual(got, tt.want) { + if got, _ := binary.MakeSliceBuffer(tt.args.c, tt.args.t, tt.args.count, tt.args.buffer); !reflect.DeepEqual(got, tt.want) { t.Errorf("MakeSliceBuffer() = %v, want %v", got, tt.want) } }) diff --git a/modeler/read.go b/modeler/read.go index be040c4..254aaab 100644 --- a/modeler/read.go +++ b/modeler/read.go @@ -5,35 +5,37 @@ import ( "fmt" "io" "reflect" + "sync" "github.com/qmuntal/gltf" "github.com/qmuntal/gltf/binary" ) -// ReadAccessor returns the data references by acr -// as an slice whose element types are the ones associated with -// acr.ComponentType and acr.Type. +// ReadAccessor returns the data references by acr as an slice +// whose element types are the ones associated with acr.ComponentType and acr.Type. // -// If data is an slice whose elements type matches the accessor type -// then data will be used as backing slice, else a new slice will be allocated. +// If buffer is not nil, it will be used as backing slice. // // ReadAccessor supports all types of accessors: non-interleaved, interleaved, sparse, // without buffer views, ..., and any combinations of them. // // ReadAccessor is safe to use even with malformed documents. // If that happens it will return an error instead of panic. -func ReadAccessor(doc *gltf.Document, acr *gltf.Accessor, buffer any) (any, error) { +func ReadAccessor(doc *gltf.Document, acr *gltf.Accessor, buffer []byte) (any, error) { if acr.BufferView == nil && acr.Sparse == nil { return nil, nil } - buffer = binary.MakeSliceBuffer(acr.ComponentType, acr.Type, acr.Count, buffer) + data, err := binary.MakeSliceBuffer(acr.ComponentType, acr.Type, acr.Count, buffer) + if err != nil { + return nil, err + } if acr.BufferView != nil { buf, err := readBufferView(doc, *acr.BufferView) if err != nil { return nil, err } byteStride := doc.BufferViews[*acr.BufferView].ByteStride - err = binary.Read(buf[acr.ByteOffset:], byteStride, buffer) + err = binary.Read(buf[acr.ByteOffset:], byteStride, data) if err != nil { return nil, err } @@ -46,7 +48,10 @@ func ReadAccessor(doc *gltf.Document, acr *gltf.Accessor, buffer any) (any, erro } byteStride := doc.BufferViews[acr.Sparse.Indices.BufferView].ByteStride - indices := binary.MakeSlice(acr.Sparse.Indices.ComponentType, gltf.AccessorScalar, acr.Sparse.Count) + indices, err := binary.MakeSlice(acr.Sparse.Indices.ComponentType, gltf.AccessorScalar, acr.Sparse.Count) + if err != nil { + return nil, err + } err = binary.Read(indicesBuffer[acr.Sparse.Indices.ByteOffset:], byteStride, indices) if err != nil { return nil, err @@ -57,13 +62,16 @@ func ReadAccessor(doc *gltf.Document, acr *gltf.Accessor, buffer any) (any, erro return nil, err } byteStride = doc.BufferViews[acr.Sparse.Values.ByteOffset].ByteStride - values := binary.MakeSlice(acr.ComponentType, acr.Type, acr.Sparse.Count) + values, err := binary.MakeSlice(acr.ComponentType, acr.Type, acr.Sparse.Count) + if err != nil { + return nil, err + } err = binary.Read(valuesBuffer[acr.Sparse.Values.ByteOffset:], byteStride, values) if err != nil { return nil, err } - s := reflect.ValueOf(buffer) + s := reflect.ValueOf(data) ind := reflect.ValueOf(indices) vals := reflect.ValueOf(values) for i := 0; i < int(acr.Sparse.Count); i++ { @@ -87,7 +95,7 @@ func ReadAccessor(doc *gltf.Document, acr *gltf.Accessor, buffer any) (any, erro s.Index(id).Set(vals.Index(i)) } } - return buffer, nil + return data, nil } func readBufferView(doc *gltf.Document, bufferViewIndex int) ([]byte, error) { @@ -98,6 +106,7 @@ func readBufferView(doc *gltf.Document, bufferViewIndex int) ([]byte, error) { } // ReadBufferView returns the slice of bytes associated with the BufferView. +// The slice is a view of the buffer data, so it is not safe to modify it. // // It is safe to use even with malformed documents. // If that happens it will return an error instead of panic. @@ -114,6 +123,22 @@ func ReadBufferView(doc *gltf.Document, bv *gltf.BufferView) ([]byte, error) { return buf[bv.ByteOffset:high], nil } +var bufPool = sync.Pool{ + New: func() any { + buf := make([]byte, 1024) + return &buf + }, +} + +func makeBufferOf[T any](count int, buffer []T) []T { + if len(buffer) < count { + buffer = append(buffer, make([]T, count-len(buffer))...) + } else { + buffer = buffer[:count] + } + return buffer +} + // ReadIndices returns the data referenced by acr. // If acr.ComponentType is other than Uint the data // will be converted appropriately. @@ -128,26 +153,24 @@ func ReadIndices(doc *gltf.Document, acr *gltf.Accessor, buffer []uint32) ([]uin if acr.Type != gltf.AccessorScalar { return nil, errAccessorType(acr.Type) } - data, err := ReadAccessor(doc, acr, buffer) + bufPtr := bufPool.Get().(*[]byte) + defer bufPool.Put(bufPtr) + data, err := ReadAccessor(doc, acr, *bufPtr) if err != nil { return nil, err } - if len(buffer) < acr.Count { - buffer = append(buffer, make([]uint32, acr.Count-len(buffer))...) - } else { - buffer = buffer[:acr.Count] - } - switch acr.ComponentType { - case gltf.ComponentUbyte: - for i, e := range data.([]uint8) { + buffer = makeBufferOf(acr.Count, buffer) + switch data := data.(type) { + case []uint8: + for i, e := range data { buffer[i] = uint32(e) } - case gltf.ComponentUshort: - for i, e := range data.([]uint16) { + case []uint16: + for i, e := range data { buffer[i] = uint32(e) } - case gltf.ComponentUint: - buffer = data.([]uint32) + case []uint32: + copy(buffer, data) } return buffer, nil } @@ -162,11 +185,15 @@ func ReadNormal(doc *gltf.Document, acr *gltf.Accessor, buffer [][3]float32) ([] if acr.Type != gltf.AccessorVec3 { return nil, errAccessorType(acr.Type) } - data, err := ReadAccessor(doc, acr, buffer) + bufPtr := bufPool.Get().(*[]byte) + defer bufPool.Put(bufPtr) + data, err := ReadAccessor(doc, acr, *bufPtr) if err != nil { return nil, err } - return data.([][3]float32), nil + buffer = makeBufferOf(acr.Count, buffer) + copy(buffer, data.([][3]float32)) + return buffer, nil } // ReadTangent returns the data referenced by acr. @@ -179,11 +206,15 @@ func ReadTangent(doc *gltf.Document, acr *gltf.Accessor, buffer [][4]float32) ([ if acr.Type != gltf.AccessorVec4 { return nil, errAccessorType(acr.Type) } - data, err := ReadAccessor(doc, acr, buffer) + bufPtr := bufPool.Get().(*[]byte) + defer bufPool.Put(bufPtr) + data, err := ReadAccessor(doc, acr, *bufPtr) if err != nil { return nil, err } - return data.([][4]float32), nil + buffer = makeBufferOf(acr.Count, buffer) + copy(buffer, data.([][4]float32)) + return buffer, nil } // ReadTextureCoord returns the data referenced by acr. @@ -200,30 +231,28 @@ func ReadTextureCoord(doc *gltf.Document, acr *gltf.Accessor, buffer [][2]float3 if acr.Type != gltf.AccessorVec2 { return nil, errAccessorType(acr.Type) } - data, err := ReadAccessor(doc, acr, buffer) + bufPtr := bufPool.Get().(*[]byte) + defer bufPool.Put(bufPtr) + data, err := ReadAccessor(doc, acr, *bufPtr) if err != nil { return nil, err } - if len(buffer) < acr.Count { - buffer = append(buffer, make([][2]float32, acr.Count-len(buffer))...) - } else { - buffer = buffer[:acr.Count] - } - switch acr.ComponentType { - case gltf.ComponentUbyte: - for i, e := range data.([][2]uint8) { + buffer = makeBufferOf(acr.Count, buffer) + switch data := data.(type) { + case [][2]uint8: + for i, e := range data { buffer[i] = [2]float32{ gltf.DenormalizeUbyte(e[0]), gltf.DenormalizeUbyte(e[1]), } } - case gltf.ComponentUshort: - for i, e := range data.([][2]uint16) { + case [][2]uint16: + for i, e := range data { buffer[i] = [2]float32{ gltf.DenormalizeUshort(e[0]), gltf.DenormalizeUshort(e[1]), } } - case gltf.ComponentFloat: - buffer = data.([][2]float32) + case [][2]float32: + copy(buffer, data) } return buffer, nil } @@ -242,32 +271,30 @@ func ReadWeights(doc *gltf.Document, acr *gltf.Accessor, buffer [][4]float32) ([ if acr.Type != gltf.AccessorVec4 { return nil, errAccessorType(acr.Type) } - data, err := ReadAccessor(doc, acr, buffer) + bufPtr := bufPool.Get().(*[]byte) + defer bufPool.Put(bufPtr) + data, err := ReadAccessor(doc, acr, *bufPtr) if err != nil { return nil, err } - if len(buffer) < acr.Count { - buffer = append(buffer, make([][4]float32, acr.Count-len(buffer))...) - } else { - buffer = buffer[:acr.Count] - } - switch acr.ComponentType { - case gltf.ComponentUbyte: - for i, e := range data.([][4]uint8) { + buffer = makeBufferOf(acr.Count, buffer) + switch data := data.(type) { + case [][4]uint8: + for i, e := range data { buffer[i] = [4]float32{ gltf.DenormalizeUbyte(e[0]), gltf.DenormalizeUbyte(e[1]), gltf.DenormalizeUbyte(e[2]), gltf.DenormalizeUbyte(e[3]), } } - case gltf.ComponentUshort: - for i, e := range data.([][4]uint16) { + case [][4]uint16: + for i, e := range data { buffer[i] = [4]float32{ gltf.DenormalizeUshort(e[0]), gltf.DenormalizeUshort(e[1]), gltf.DenormalizeUshort(e[2]), gltf.DenormalizeUshort(e[3]), } } - case gltf.ComponentFloat: - buffer = data.([][4]float32) + case [][4]float32: + copy(buffer, data) } return buffer, nil } @@ -286,25 +313,23 @@ func ReadJoints(doc *gltf.Document, acr *gltf.Accessor, buffer [][4]uint16) ([][ if acr.Type != gltf.AccessorVec4 { return nil, errAccessorType(acr.Type) } - data, err := ReadAccessor(doc, acr, buffer) + bufPtr := bufPool.Get().(*[]byte) + defer bufPool.Put(bufPtr) + data, err := ReadAccessor(doc, acr, *bufPtr) if err != nil { return nil, err } - if len(buffer) < acr.Count { - buffer = append(buffer, make([][4]uint16, acr.Count-len(buffer))...) - } else { - buffer = buffer[:acr.Count] - } - switch acr.ComponentType { - case gltf.ComponentUbyte: - for i, e := range data.([][4]uint8) { + buffer = makeBufferOf(acr.Count, buffer) + switch data := data.(type) { + case [][4]uint8: + for i, e := range data { buffer[i] = [4]uint16{ uint16(e[0]), uint16(e[1]), uint16(e[2]), uint16(e[3]), } } - case gltf.ComponentUshort: - buffer = data.([][4]uint16) + case [][4]uint16: + copy(buffer, data) } return buffer, nil } @@ -319,11 +344,15 @@ func ReadPosition(doc *gltf.Document, acr *gltf.Accessor, buffer [][3]float32) ( if acr.Type != gltf.AccessorVec3 { return nil, errAccessorType(acr.Type) } - data, err := ReadAccessor(doc, acr, buffer) + bufPtr := bufPool.Get().(*[]byte) + defer bufPool.Put(bufPtr) + data, err := ReadAccessor(doc, acr, *bufPtr) if err != nil { return nil, err } - return data.([][3]float32), nil + buffer = makeBufferOf(acr.Count, buffer) + copy(buffer, data.([][3]float32)) + return buffer, nil } // ReadColor returns the data referenced by acr. @@ -342,44 +371,36 @@ func ReadColor(doc *gltf.Document, acr *gltf.Accessor, buffer [][4]uint8) ([][4] default: return nil, errAccessorType(acr.Type) } - data, err := ReadAccessor(doc, acr, buffer) + bufPtr := bufPool.Get().(*[]byte) + defer bufPool.Put(bufPtr) + data, err := ReadAccessor(doc, acr, *bufPtr) if err != nil { return nil, err } - if len(buffer) < acr.Count { - buffer = append(buffer, make([][4]uint8, acr.Count-len(buffer))...) - } else { - buffer = buffer[:acr.Count] - } - switch acr.ComponentType { - case gltf.ComponentUbyte: - if acr.Type == gltf.AccessorVec3 { - for i, e := range data.([][3]uint8) { - buffer[i] = [4]uint8{e[0], e[1], e[2], 255} - } - } else { - buffer = data.([][4]uint8) + buffer = makeBufferOf(acr.Count, buffer) + switch data := data.(type) { + case [][3]uint8: + for i, e := range data { + buffer[i] = [4]uint8{e[0], e[1], e[2], 255} } - case gltf.ComponentUshort: - if acr.Type == gltf.AccessorVec3 { - for i, e := range data.([][3]uint16) { - buffer[i] = [4]uint8{uint8(e[0]), uint8(e[1]), uint8(e[2]), 255} - } - } else { - for i, e := range data.([][4]uint16) { - buffer[i] = [4]uint8{uint8(e[0]), uint8(e[1]), uint8(e[2]), uint8(e[3])} - } + case [][4]uint8: + copy(buffer, data) + case [][3]uint16: + for i, e := range data { + buffer[i] = [4]uint8{uint8(e[0]), uint8(e[1]), uint8(e[2]), 255} } - case gltf.ComponentFloat: - if acr.Type == gltf.AccessorVec3 { - for i, e := range data.([][3]float32) { - tmp := gltf.NormalizeRGB(e) - buffer[i] = [4]uint8{tmp[0], tmp[1], tmp[2], 255} - } - } else { - for i, e := range data.([][4]float32) { - buffer[i] = gltf.NormalizeRGBA(e) - } + case [][4]uint16: + for i, e := range data { + buffer[i] = [4]uint8{uint8(e[0]), uint8(e[1]), uint8(e[2]), uint8(e[3])} + } + case [][3]float32: + for i, e := range data { + tmp := gltf.NormalizeRGB(e) + buffer[i] = [4]uint8{tmp[0], tmp[1], tmp[2], 255} + } + case [][4]float32: + for i, e := range data { + buffer[i] = gltf.NormalizeRGBA(e) } } return buffer, nil @@ -401,53 +422,46 @@ func ReadColor64(doc *gltf.Document, acr *gltf.Accessor, buffer [][4]uint16) ([] default: return nil, errAccessorType(acr.Type) } - data, err := ReadAccessor(doc, acr, buffer) + bufPtr := bufPool.Get().(*[]byte) + defer bufPool.Put(bufPtr) + data, err := ReadAccessor(doc, acr, *bufPtr) if err != nil { return nil, err } - if len(buffer) < acr.Count { - buffer = append(buffer, make([][4]uint16, acr.Count-len(buffer))...) - } else { - buffer = buffer[:acr.Count] - } - switch acr.ComponentType { - case gltf.ComponentUbyte: - if acr.Type == gltf.AccessorVec3 { - for i, e := range data.([][3]uint8) { - buffer[i] = [4]uint16{ - uint16(e[0]) | uint16(e[0])<<8, - uint16(e[1]) | uint16(e[1])<<8, - uint16(e[2]) | uint16(e[2])<<8, - 65535} - } - } else { - for i, e := range data.([][4]uint8) { - buffer[i] = [4]uint16{ - uint16(e[0]) | uint16(e[0])<<8, - uint16(e[1]) | uint16(e[1])<<8, - uint16(e[2]) | uint16(e[2])<<8, - uint16(e[3]) | uint16(e[3])<<8, - } + buffer = makeBufferOf(acr.Count, buffer) + switch data := data.(type) { + case [][3]uint8: + for i, e := range data { + buffer[i] = [4]uint16{ + uint16(e[0]) | uint16(e[0])<<8, + uint16(e[1]) | uint16(e[1])<<8, + uint16(e[2]) | uint16(e[2])<<8, + 65535, } } - case gltf.ComponentUshort: - if acr.Type == gltf.AccessorVec3 { - for i, e := range data.([][3]uint16) { - buffer[i] = [4]uint16{e[0], e[1], e[2], 65535} + case [][4]uint8: + for i, e := range data { + buffer[i] = [4]uint16{ + uint16(e[0]) | uint16(e[0])<<8, + uint16(e[1]) | uint16(e[1])<<8, + uint16(e[2]) | uint16(e[2])<<8, + uint16(e[3]) | uint16(e[3])<<8, } - } else { - buffer = data.([][4]uint16) } - case gltf.ComponentFloat: - if acr.Type == gltf.AccessorVec3 { - for i, e := range data.([][3]float32) { - tmp := gltf.NormalizeRGB64(e) - buffer[i] = [4]uint16{tmp[0], tmp[1], tmp[2], 65535} - } - } else { - for i, e := range data.([][4]float32) { - buffer[i] = gltf.NormalizeRGBA64(e) - } + case [][3]uint16: + for i, e := range data { + buffer[i] = [4]uint16{e[0], e[1], e[2], 65535} + } + case [][4]uint16: + copy(buffer, data) + case [][3]float32: + for i, e := range data { + tmp := gltf.NormalizeRGB64(e) + buffer[i] = [4]uint16{tmp[0], tmp[1], tmp[2], 65535} + } + case [][4]float32: + for i, e := range data { + buffer[i] = gltf.NormalizeRGBA64(e) } } return buffer, nil diff --git a/modeler/read_test.go b/modeler/read_test.go index 42b484b..585252f 100644 --- a/modeler/read_test.go +++ b/modeler/read_test.go @@ -202,7 +202,7 @@ func TestReadAccessorAllocs(t *testing.T) { BufferView: gltf.Index(0), ComponentType: gltf.ComponentFloat, Type: gltf.AccessorVec3, Count: 4, } - testFunc := func(t *testing.T, buf [][3]float32, want float32) { + testFunc := func(t *testing.T, buf []byte, want float32) { allocs := testing.AllocsPerRun(10, func() { modeler.ReadAccessor(doc, acr, buf) }) @@ -215,14 +215,14 @@ func TestReadAccessorAllocs(t *testing.T) { testFunc(t, nil, 2) }) t.Run("2", func(t *testing.T) { - buf := make([][3]float32, 2) + buf := make([]byte, 24) testFunc(t, buf, 6) testFunc(t, buf, 6) testFunc(t, buf, 6) testFunc(t, buf, 6) }) t.Run("4", func(t *testing.T) { - buf := make([][3]float32, 4) + buf := make([]byte, 48) testFunc(t, buf, 1) testFunc(t, buf, 1) testFunc(t, buf, 1)