Animation & blend file: Preliminary support for actions.

This commit is contained in:
Alberto Torres 2024-12-16 21:34:55 +01:00
parent fc39a93a94
commit 45cc6dac00
2 changed files with 110 additions and 0 deletions

View file

@ -293,10 +293,70 @@ proc idPropertiesToJsonTable(prop: FNode): Table[string, JsonNode] =
else: discard
prop = prop.next
proc loadFCurveImpl(self: BlendLoader, fcurve: FNode): (string, AnimationChannel) =
let path = fcurve.rna_path.str
let chan = new AnimationChannel
let path_parts = path.rsplit('.',1)
if path_parts.len == 1:
chan.channel_type = ChObject
else:
if path.startswith "key_blocks[":
chan.channel_type = ChShape
chan.name = path.split('"')[1]
elif path.startswith "pose.bones[":
return # TODO
else:
echo "Warning: unknown channel path ", path
return
chan.property = case path_parts[^1]:
of "value": PropValue
of "location": PropPosition
of "rotation_euler": PropRotationEuler
of "rotation_quaternion": PropRotationQuaternion
of "scale": PropScale
else:
echo "Warning: unknown property ", path
return
chan.index = fcurve.array_index.i32[0]
if chan.property == PropRotationQuaternion:
chan.index = (chan.index - 1) and 3
let count = fcurve.totvert.i32[0]
for i,floats in fcurve.bezt[0 ..< count].vec:
let fs = floats.f32
chan.points.add BezierPoint(
# left_handle: vec2(fs[0], fs[1]),
co: vec2(fs[3], fs[4]),
# right_handle: vec2(fs[6], fs[7]),
)
if chan.property != PropValue:
return (&"{path}[{chan.index}]", chan)
else:
return (path, chan)
proc loadActionImpl(self: BlendLoader, acn: FNode): Action =
new(result)
let flags = acn.flag.i32[0]
result.manual_range = (flags and 4096).bool
result.frame_start = acn.frame_start.f32[0]
result.frame_end = acn.frame_end.f32[0]
var curve = acn.curves.first
while curve.valid:
let (path, ch) = self.loadFCurveImpl(curve)
if ch != nil:
result.channels[path] = ch
curve = curve.next
proc loadAction*(self: BlendLoader, name: string): Action =
for n in self.blend_file.named_blocks["AC"]:
if n.id.name.str.strip2 == name:
return self.loadActionImpl(n)
raise KeyError.newException &"Could not find action '{name}'"
proc loadObjectImpl(self: BlendLoader, scene: Scene, obn: FNode): (GameObject, string) =
let name = obn.id.name.str.strip2
let data = obn.data
let ob = case obn["type"].i16[0]:
var shape_key_adt: FNode
of BMesh.int16:
var ob = self.engine.new_mesh(name=name)
let mat_count = obn.totcol.i32[0]
@ -377,6 +437,8 @@ proc loadObjectImpl(self: BlendLoader, scene: Scene, obn: FNode): (GameObject, s
if v.vtype == Tangent and v.attname notin tangents:
tangents.add v.attname
if data.key.valid:
shape_key_adt = data.key.adt
# TODO: defer this by storing the name of the mesh as hash
# (maybe the address too)
@ -514,6 +576,20 @@ proc loadObjectImpl(self: BlendLoader, scene: Scene, obn: FNode): (GameObject, s
ob.object_color = obn.col.f32.vec4
var animation_datas: seq[FNode]
for adt in [obn.adt, shape_key_adt]:
if adt.valid:
animation_datas.add adt
# TODO: make animation strips. we'll just merge the actions for now.
for adt in animation_datas:
if adt.action.valid:
let ac = self.loadActionImpl(adt.action)
if ob.action == nil:
ob.action = ac
else:
for k,v in ac.channels:
ob.action.channels[k] = v
let prop = obn.id.properties
if prop.valid:
ob.properties = idPropertiesToJsonTable(prop.data.group.first)

View file

@ -125,6 +125,7 @@ type
# probe_cube*: Probe
# probe_planar*: Probe
properties*: Table[string, JsonNode]
action*: Action # TODO: remove when strips are implemented
# animation_strips*: seq[AnimationStrip]
name*: string
original_name*: string
@ -823,6 +824,39 @@ type
cache_dir_bc*: string ## Cache directory for writing bc
cache_dir_astc*: string ## Cache directory for writing astc
# action.nim
Action* = ref object
manual_range*: bool
frame_start*, frame_end*: float32
channels*: OrderedTable[string, AnimationChannel]
ChannelType* = enum
ChObject
ChPose
ChShape
ChObProperty
ChannelProperty* = enum
PropValue
PropPosition
PropRotationEuler
PropRotationQuaternion
PropScale
AnimationChannel* = ref object
channel_type*: ChannelType
name*: string
property*: ChannelProperty
property_name*: string
index*: int
points*: seq[BezierPoint]
last_eval_point*: int
BezierPoint* = object
# left_handle*: Vec2
co*: Vec2
# right_handle*: Vec2
# INCOMPLETE