mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 13:56:32 +08:00
This commit is contained in:
1 parent
13dbacad15
commit
f71000befd
45 files changed
+4383
-422
No files matched your search
@@ -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()
|
||||
}
|
||||
Reference in new issue
Block a user