qmuntal/gltf

View on GitHub
modeler/read.go

Summary

Maintainability
D
1 day
Test Coverage
package modeler

import (
    "errors"
    "fmt"
    "io"
    "reflect"

    "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.
//
// 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.
//
// 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) {
    if acr.BufferView == nil && acr.Sparse == nil {
        return nil, nil
    }
    buffer = binary.MakeSliceBuffer(acr.ComponentType, acr.Type, acr.Count, buffer)
    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)
        if err != nil {
            return nil, err
        }
    }

    if acr.Sparse != nil {
        indicesBuffer, err := readBufferView(doc, acr.Sparse.Indices.BufferView)
        if err != nil {
            return nil, err
        }

        byteStride := doc.BufferViews[acr.Sparse.Indices.BufferView].ByteStride
        indices := binary.MakeSlice(acr.Sparse.Indices.ComponentType, gltf.AccessorScalar, acr.Sparse.Count)
        err = binary.Read(indicesBuffer[acr.Sparse.Indices.ByteOffset:], byteStride, indices)
        if err != nil {
            return nil, err
        }

        valuesBuffer, err := readBufferView(doc, acr.Sparse.Values.BufferView)
        if err != nil {
            return nil, err
        }
        byteStride = doc.BufferViews[acr.Sparse.Values.ByteOffset].ByteStride
        values := binary.MakeSlice(acr.ComponentType, acr.Type, acr.Sparse.Count)
        err = binary.Read(valuesBuffer[acr.Sparse.Values.ByteOffset:], byteStride, values)
        if err != nil {
            return nil, err
        }

        s := reflect.ValueOf(buffer)
        ind := reflect.ValueOf(indices)
        vals := reflect.ValueOf(values)
        tp := reflect.TypeOf((*int)(nil)).Elem()
        for i := 0; i < int(acr.Sparse.Count); i++ {
            id := ind.Index(i).Convert(tp).Interface().(int)
            s.Index(id).Set(vals.Index(i))
        }
    }
    return buffer, nil
}

func readBufferView(doc *gltf.Document, bufferViewIndex uint32) ([]byte, error) {
    if uint32(len(doc.BufferViews)) <= bufferViewIndex {
        return nil, errors.New("gltf: bufferview index overflows")
    }
    return ReadBufferView(doc, doc.BufferViews[bufferViewIndex])
}

// ReadBufferView returns the slice of bytes associated with the BufferView.
//
// It is safe to use even with malformed documents.
// If that happens it will return an error instead of panic.
func ReadBufferView(doc *gltf.Document, bv *gltf.BufferView) ([]byte, error) {
    if uint32(len(doc.Buffers)) <= bv.Buffer {
        return nil, errors.New("gltf: buffer index overflows")
    }
    buf := doc.Buffers[bv.Buffer].Data

    high := bv.ByteOffset + bv.ByteLength
    if uint32(len(buf)) < high {
        return nil, io.ErrShortBuffer
    }
    return buf[bv.ByteOffset:high], nil
}

// ReadIndices returns the data referenced by acr.
// If acr.ComponentType is other than Uint the data
// will be converted appropriately.
//
// See ReadAccessor for more info.
func ReadIndices(doc *gltf.Document, acr *gltf.Accessor, buffer []uint32) ([]uint32, error) {
    switch acr.ComponentType {
    case gltf.ComponentUbyte, gltf.ComponentUshort, gltf.ComponentUint:
    default:
        return nil, errComponentType(acr.ComponentType)
    }
    if acr.Type != gltf.AccessorScalar {
        return nil, errAccessorType(acr.Type)
    }
    data, err := ReadAccessor(doc, acr, buffer)
    if err != nil {
        return nil, err
    }
    if uint32(len(buffer)) < acr.Count {
        buffer = append(buffer, make([]uint32, acr.Count-uint32(len(buffer)))...)
    } else {
        buffer = buffer[:acr.Count]
    }
    switch acr.ComponentType {
    case gltf.ComponentUbyte:
        for i, e := range data.([]uint8) {
            buffer[i] = uint32(e)
        }
    case gltf.ComponentUshort:
        for i, e := range data.([]uint16) {
            buffer[i] = uint32(e)
        }
    case gltf.ComponentUint:
        buffer = data.([]uint32)
    }
    return buffer, nil
}

// ReadNormal returns the data referenced by acr.
//
// See ReadAccessor for more info.
func ReadNormal(doc *gltf.Document, acr *gltf.Accessor, buffer [][3]float32) ([][3]float32, error) {
    if acr.ComponentType != gltf.ComponentFloat {
        return nil, errComponentType(acr.ComponentType)
    }
    if acr.Type != gltf.AccessorVec3 {
        return nil, errAccessorType(acr.Type)
    }
    data, err := ReadAccessor(doc, acr, buffer)
    if err != nil {
        return nil, err
    }
    return data.([][3]float32), nil
}

// ReadTangent returns the data referenced by acr.
//
// See ReadAccessor for more info.
func ReadTangent(doc *gltf.Document, acr *gltf.Accessor, buffer [][4]float32) ([][4]float32, error) {
    if acr.ComponentType != gltf.ComponentFloat {
        return nil, errComponentType(acr.ComponentType)
    }
    if acr.Type != gltf.AccessorVec4 {
        return nil, errAccessorType(acr.Type)
    }
    data, err := ReadAccessor(doc, acr, buffer)
    if err != nil {
        return nil, err
    }
    return data.([][4]float32), nil
}

// ReadTextureCoord returns the data referenced by acr.
// If acr.ComponentType is other than Float the data
// will be converted and denormalized appropriately.
//
// See ReadAccessor for more info.
func ReadTextureCoord(doc *gltf.Document, acr *gltf.Accessor, buffer [][2]float32) ([][2]float32, error) {
    switch acr.ComponentType {
    case gltf.ComponentUbyte, gltf.ComponentUshort, gltf.ComponentFloat:
    default:
        return nil, errComponentType(acr.ComponentType)
    }
    if acr.Type != gltf.AccessorVec2 {
        return nil, errAccessorType(acr.Type)
    }
    data, err := ReadAccessor(doc, acr, buffer)
    if err != nil {
        return nil, err
    }
    if uint32(len(buffer)) < acr.Count {
        buffer = append(buffer, make([][2]float32, acr.Count-uint32(len(buffer)))...)
    } else {
        buffer = buffer[:acr.Count]
    }
    switch acr.ComponentType {
    case gltf.ComponentUbyte:
        for i, e := range data.([][2]uint8) {
            buffer[i] = [2]float32{
                gltf.DenormalizeUbyte(e[0]), gltf.DenormalizeUbyte(e[1]),
            }
        }
    case gltf.ComponentUshort:
        for i, e := range data.([][2]uint16) {
            buffer[i] = [2]float32{
                gltf.DenormalizeUshort(e[0]), gltf.DenormalizeUshort(e[1]),
            }
        }
    case gltf.ComponentFloat:
        buffer = data.([][2]float32)
    }
    return buffer, nil
}

// ReadWeights returns the data referenced by acr.
// If acr.ComponentType is other than Float the data
// will be converted and denormalized appropriately.
//
// See ReadAccessor for more info.
func ReadWeights(doc *gltf.Document, acr *gltf.Accessor, buffer [][4]float32) ([][4]float32, error) {
    switch acr.ComponentType {
    case gltf.ComponentUbyte, gltf.ComponentUshort, gltf.ComponentFloat:
    default:
        return nil, errComponentType(acr.ComponentType)
    }
    if acr.Type != gltf.AccessorVec4 {
        return nil, errAccessorType(acr.Type)
    }
    data, err := ReadAccessor(doc, acr, buffer)
    if err != nil {
        return nil, err
    }
    if uint32(len(buffer)) < acr.Count {
        buffer = append(buffer, make([][4]float32, acr.Count-uint32(len(buffer)))...)
    } else {
        buffer = buffer[:acr.Count]
    }
    switch acr.ComponentType {
    case gltf.ComponentUbyte:
        for i, e := range data.([][4]uint8) {
            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) {
            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)
    }
    return buffer, nil
}

// ReadJoints returns the data referenced by acr.
// If acr.ComponentType is other than Ushort the data
// will be converted and denormalized appropriately.
//
// See ReadAccessor for more info.
func ReadJoints(doc *gltf.Document, acr *gltf.Accessor, buffer [][4]uint16) ([][4]uint16, error) {
    switch acr.ComponentType {
    case gltf.ComponentUbyte, gltf.ComponentUshort:
    default:
        return nil, errComponentType(acr.ComponentType)
    }
    if acr.Type != gltf.AccessorVec4 {
        return nil, errAccessorType(acr.Type)
    }
    data, err := ReadAccessor(doc, acr, buffer)
    if err != nil {
        return nil, err
    }
    if uint32(len(buffer)) < acr.Count {
        buffer = append(buffer, make([][4]uint16, acr.Count-uint32(len(buffer)))...)
    } else {
        buffer = buffer[:acr.Count]
    }
    switch acr.ComponentType {
    case gltf.ComponentUbyte:
        for i, e := range data.([][4]uint8) {
            buffer[i] = [4]uint16{
                uint16(e[0]), uint16(e[1]),
                uint16(e[2]), uint16(e[3]),
            }
        }
    case gltf.ComponentUshort:
        buffer = data.([][4]uint16)
    }
    return buffer, nil
}

// ReadPosition returns the data referenced by acr.
//
// See ReadAccessor for more info.
func ReadPosition(doc *gltf.Document, acr *gltf.Accessor, buffer [][3]float32) ([][3]float32, error) {
    if acr.ComponentType != gltf.ComponentFloat {
        return nil, errComponentType(acr.ComponentType)
    }
    if acr.Type != gltf.AccessorVec3 {
        return nil, errAccessorType(acr.Type)
    }
    data, err := ReadAccessor(doc, acr, buffer)
    if err != nil {
        return nil, err
    }
    return data.([][3]float32), nil
}

// ReadColor returns the data referenced by acr.
// If acr.ComponentType is other than Ubyte the data
// will be converted and normalized appropriately.
//
// See ReadAccessor for more info.
func ReadColor(doc *gltf.Document, acr *gltf.Accessor, buffer [][4]uint8) ([][4]uint8, error) {
    switch acr.ComponentType {
    case gltf.ComponentUbyte, gltf.ComponentUshort, gltf.ComponentFloat:
    default:
        return nil, errComponentType(acr.ComponentType)
    }
    switch acr.Type {
    case gltf.AccessorVec3, gltf.AccessorVec4:
    default:
        return nil, errAccessorType(acr.Type)
    }
    data, err := ReadAccessor(doc, acr, buffer)
    if err != nil {
        return nil, err
    }
    if uint32(len(buffer)) < acr.Count {
        buffer = append(buffer, make([][4]uint8, acr.Count-uint32(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)
        }
    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 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)
            }
        }
    }
    return buffer, nil
}

// ReadColor returns the data referenced by acr.
// If acr.ComponentType is other than Ushort the data
// will be converted and normalized appropriately.
//
// See ReadAccessor for more info.
func ReadColor64(doc *gltf.Document, acr *gltf.Accessor, buffer [][4]uint16) ([][4]uint16, error) {
    switch acr.ComponentType {
    case gltf.ComponentUbyte, gltf.ComponentUshort, gltf.ComponentFloat:
    default:
        return nil, errComponentType(acr.ComponentType)
    }
    switch acr.Type {
    case gltf.AccessorVec3, gltf.AccessorVec4:
    default:
        return nil, errAccessorType(acr.Type)
    }
    data, err := ReadAccessor(doc, acr, buffer)
    if err != nil {
        return nil, err
    }
    if uint32(len(buffer)) < acr.Count {
        buffer = append(buffer, make([][4]uint16, acr.Count-uint32(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,
                }
            }
        }
    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}
            }
        } 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)
            }
        }
    }
    return buffer, nil
}

func errAccessorType(tp gltf.AccessorType) error {
    return fmt.Errorf("gltf: accessor type %v not allowed", tp)
}

func errComponentType(tp gltf.ComponentType) error {
    return fmt.Errorf("gltf: component type %v not allowed", tp)
}