Files
ofdgo/internal/base/threed/u3d_texture.go
T
2026-10-14 00:44:44 +08:00

182 lines
5.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright 2025-2026 肖其顿 (XIAO QI DUN)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package threed
import (
"fmt"
)
// U3DTexture 保存纹理尺寸、图像类型及各通道的图像数据
type U3DTexture struct {
Name string
Width, Height uint32
ImageType uint8
Images []U3DTextureImage
}
// U3DTextureImage 保存压缩格式、通道位图及完整压缩数据,外部图像仅保留候选URL
// Compression依次以1至4表示JPEG-24、PNG、JPEG-8和TIFF,ByteCount仅用于内置图像
type U3DTextureImage struct {
Compression uint8
Channels uint8
External bool
ByteCount uint32
Data []byte
URLs []string
}
// u3dTextureChannels 将亮度通道展开为RGB覆盖位,保留透明度位
// 入参: channels 标准通道位图
// 返回: uint8 实际覆盖通道
func u3dTextureChannels(channels uint8) uint8 {
if channels&16 != 0 {
channels = channels&^16 | 14
}
return channels
}
// textureDeclaration 按ECMA-363第9.8.5.1节读取纹理及通道图像声明
// 入参: r 字段读取器, name 纹理名称
// 返回: error 格式、预算或取消错误
func (d *u3dDecoder) textureDeclaration(r *u3dValues, name string) error {
if _, exists := d.textures[name]; exists {
return fmt.Errorf("duplicate U3D texture %q", name)
}
texture := U3DTexture{Name: name, Height: r.u32(), Width: r.u32(), ImageType: r.u8()}
if texture.Width == 0 || texture.Height == 0 {
return fmt.Errorf("invalid U3D texture dimensions")
}
switch texture.ImageType {
case 1, 14, 15, 16, 17:
default:
return fmt.Errorf("invalid U3D texture image type")
}
count := r.u32()
if r.err != nil {
return r.err
}
if count == 0 || count > 4 || uint64(count) > uint64(len(r.data)-r.pos)/8 {
return fmt.Errorf("invalid U3D continuation image count")
}
if err := d.reserve(uint64(count), 64); err != nil {
return err
}
texture.Images = make([]U3DTextureImage, int(count))
var covered uint8
for n := range texture.Images {
if err := d.ctx.Err(); err != nil {
return err
}
image := &texture.Images[n]
image.Compression, image.Channels = r.u8(), r.u8()
attributes := uint16(r.u8()) | uint16(r.u8())<<8
if r.err != nil {
return r.err
}
channels := u3dTextureChannels(image.Channels)
if image.Compression < 1 || image.Compression > 4 || image.Channels == 0 || image.Channels&^uint8(31) != 0 || attributes > 1 || covered&channels != 0 || channels&^u3dTextureChannels(texture.ImageType) != 0 {
return fmt.Errorf("invalid U3D continuation image format")
}
covered |= channels
image.External = attributes == 1
if !image.External {
image.ByteCount = r.u32()
continue
}
count := r.u32()
if r.err != nil {
return r.err
}
if uint64(count) > uint64(len(r.data)-r.pos)/2 {
return fmt.Errorf("invalid U3D texture URL count")
}
if err := d.reserve(uint64(count), 16); err != nil {
return err
}
image.URLs = make([]string, int(count))
for i := range image.URLs {
if err := d.ctx.Err(); err != nil {
return err
}
image.URLs[i] = r.text()
if r.err != nil {
return r.err
}
}
}
if err := r.done(); err != nil {
return err
}
if covered != u3dTextureChannels(texture.ImageType) {
return fmt.Errorf("incomplete U3D texture channels")
}
if d.textures == nil {
d.textures = make(map[string]int)
}
d.textures[name] = len(d.model.Textures)
d.model.Textures = append(d.model.Textures, texture)
return nil
}
// textureContinuation 按声明长度合并纹理分片,不与输入共享存储
// 入参: r 字段读取器, name 纹理名称
// 返回: error 格式、预算或取消错误
func (d *u3dDecoder) textureContinuation(r *u3dValues, name string) error {
index := r.u32()
if r.err != nil {
return r.err
}
texture, exists := d.textures[name]
if !exists || uint64(index) >= uint64(len(d.model.Textures[texture].Images)) {
return fmt.Errorf("invalid U3D texture continuation reference")
}
image := &d.model.Textures[texture].Images[index]
data := r.take(len(r.data) - r.pos)
if image.External || uint64(len(image.Data))+uint64(len(data)) > uint64(image.ByteCount) {
return fmt.Errorf("invalid U3D texture continuation length")
}
if image.Data == nil {
if err := d.reserve(uint64(image.ByteCount), 1); err != nil {
return err
}
image.Data = make([]byte, 0, int(image.ByteCount))
}
for len(data) != 0 {
if err := d.ctx.Err(); err != nil {
return err
}
count := min(65536, len(data))
image.Data = append(image.Data, data[:count]...)
data = data[count:]
}
return d.ctx.Err()
}
// checkTextures 确认各通道的内置图像完整,不加载外部引用
// 返回: error 缺失分片或取消错误
func (d *u3dDecoder) checkTextures() error {
for _, texture := range d.model.Textures {
for _, image := range texture.Images {
if err := d.ctx.Err(); err != nil {
return err
}
if !image.External && uint64(len(image.Data)) != uint64(image.ByteCount) {
return fmt.Errorf("incomplete U3D texture %q", texture.Name)
}
}
}
return nil
}