feat(渲染能力): 完善三维渲染、字体及版本保存
build.yaml / build (push) Successful in 2m39s

This commit is contained in:
xiaoqidun committed 2026-10-11 10:20:14 +08:00
1 parent 13dbacad15
commit f71000befd
45 files changed
+4383 -422

No files matched your search

+176
View File
@@ -0,0 +1,176 @@
// 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 ofdgo
import (
"context"
"fmt"
"io"
)
// U3DAnimation 保存ECMA-363第9.7.3节的动画修饰器,Index为链内位置
// Attributes的1、2、4、8分别表示启动、锁定根骨骼、单轨播放和动作间平滑过渡
// TimeScale为整体时间倍率,BlendTime以毫秒计;Motions为空时使用无轨道的默认动作
type U3DAnimation struct {
Index, Attributes uint32
TimeScale, BlendTime float32
Motions []U3DAnimationMotion
}
// U3DAnimationMotion 保存动画中的动作引用,空名称引用默认动作
// Attributes的1、2分别表示循环和同步结束,TimeOffset以毫秒计,TimeScale为动作时间倍率
type U3DAnimationMotion struct {
Name string
Attributes uint32
TimeOffset, TimeScale float32
}
// animation 读取动画参数及有序动作引用,不在解码时调度或求值
// 入参: r 字段读取器, name 所属链名, chain 链类型, index 链内位置
// 返回: error 结构、参数、预算或取消错误
func (d *u3dDecoder) animation(r *u3dValues, name string, chain int, index uint32) error {
animation := U3DAnimation{Index: index, Attributes: r.u32(), TimeScale: r.f32()}
count := r.u32()
if r.err != nil {
return r.err
}
if len(r.data)-r.pos < 4 || uint64(count) > uint64(len(r.data)-r.pos-4)/14 {
return io.ErrUnexpectedEOF
}
if err := d.reserve(uint64(count), 32); err != nil {
return err
}
if count != 0 {
animation.Motions = make([]U3DAnimationMotion, int(count))
}
for i := range animation.Motions {
if i%256 == 0 {
if err := d.ctx.Err(); err != nil {
return err
}
}
motion := &animation.Motions[i]
motion.Name, motion.Attributes = r.text(), r.u32()
motion.TimeOffset, motion.TimeScale = r.f32(), r.f32()
if r.err != nil {
return r.err
}
}
animation.BlendTime = r.f32()
if err := r.done(); err != nil {
return err
}
if err := animation.validate(d.ctx); err != nil {
return err
}
if chain == 1 {
mesh := &d.meshes[name].mesh
mesh.Animations = append(mesh.Animations, animation)
} else {
node := &d.model.Nodes[d.nodes[name]]
node.Animations = append(node.Animations, animation)
}
return nil
}
// validate 校验动画属性及有限数值,保留零倍率与反向播放倍率
// 入参: ctx 取消上下文
// 返回: error 参数或取消错误
func (animation U3DAnimation) validate(ctx context.Context) error {
if err := ctx.Err(); err != nil {
return err
}
if animation.Index == 0 || animation.Attributes & ^uint32(15) != 0 || !finite(float64(animation.TimeScale)) || !finite(float64(animation.BlendTime)) || animation.BlendTime < 0 {
return fmt.Errorf("invalid U3D animation parameters")
}
for i, motion := range animation.Motions {
if i%256 == 0 {
if err := ctx.Err(); err != nil {
return err
}
}
if motion.Attributes & ^uint32(3) != 0 || !finite(float64(motion.TimeOffset)) || !finite(float64(motion.TimeScale)) {
return fmt.Errorf("invalid U3D animation motion parameters")
}
}
return nil
}
// checkAnimations 校验全部动作引用,仅对接入场景的修饰器检查求值能力
// 返回: error 引用、能力或取消错误
func (r *u3dRender) checkAnimations() error {
check := func(animations []U3DAnimation, evaluate bool) error {
for _, animation := range animations {
if err := animation.validate(r.ctx); err != nil {
return err
}
for i, motion := range animation.Motions {
if i%256 == 0 {
if err := r.ctx.Err(); err != nil {
return err
}
}
if motion.Name == "" {
continue
}
index, exists := r.motionIndex[motion.Name]
if !exists {
return fmt.Errorf("missing U3D animation motion %q", motion.Name)
}
if evaluate && len(animation.Motions) > 1 && len(r.model.Motions[index].Tracks) != 0 {
return fmt.Errorf("unsupported U3D animation evaluation for motion %q", motion.Name)
}
}
}
return nil
}
var active []bool
if r.connected != nil {
if err := r.reserve(uint64(len(r.model.Meshes)), 1); err != nil {
return err
}
defer func() { r.remaining += uint64(len(active)) }()
active = make([]bool, len(r.model.Meshes))
for i, node := range r.model.Nodes {
if i%256 == 0 {
if err := r.ctx.Err(); err != nil {
return err
}
}
if r.connected[i] && node.Kind == "Model" {
if mesh, exists := r.meshes[node.Resource]; exists {
active[mesh] = true
}
}
}
}
for i := range r.model.Meshes {
if err := r.ctx.Err(); err != nil {
return err
}
if err := check(r.model.Meshes[i].Animations, active == nil || active[i]); err != nil {
return err
}
}
for i := range r.model.Nodes {
if err := r.ctx.Err(); err != nil {
return err
}
if err := check(r.model.Nodes[i].Animations, r.connected == nil || r.connected[i]); err != nil {
return err
}
}
return r.ctx.Err()
}