From 45cc6dac0073345942207bd6eb570a62a25193fd Mon Sep 17 00:00:00 2001 From: Alberto Torres Date: Mon, 16 Dec 2024 21:34:55 +0100 Subject: [PATCH] Animation & blend file: Preliminary support for actions. --- src/loaders/blend.nim | 76 +++++++++++++++++++++++++++++++++++++++++++ src/types.nim | 34 +++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/src/loaders/blend.nim b/src/loaders/blend.nim index a2d0338..134fe83 100644 --- a/src/loaders/blend.nim +++ b/src/loaders/blend.nim @@ -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) diff --git a/src/types.nim b/src/types.nim index e8eb8e8..920de0e 100644 --- a/src/types.nim +++ b/src/types.nim @@ -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