1 /**
2     Inochi2D Bone Nodes
3 
4     Copyright: 
5         Copyright © 2020-2026, Inochi2D Project
6     
7     License:
8         $(LINK2 https://github.com/Inochi2D/inochi2d/blob/main/LICENSE, BSD 2-clause License)
9     
10     Authors:
11         Luna Nielsen
12 */
13 module inochi2d.nodes.bone.bone;
14 import inochi2d.core.math.deform;
15 import inochi2d.core.math.simd;
16 import inochi2d.core.math;
17 import inochi2d.nodes;
18 import nulib.collections;
19 import numem;
20 
21 /**
22     A bone, bones are used to build skeletal hirearchies that deform
23     other nodes via the use of bone weights.
24 */
25 @TypeIdAbstract
26 @TypeId("Bone", IN_MAKE_TAG!(0, 3))
27 class Bone : Node {
28 private:
29 @nogc:
30     vector!BoneTarget targets_;
31     vec2[] boneOffsets;
32 
33     /**
34         Finds the given target's index within the bone mapping.
35 
36         Params:
37             toFind = The deformable to find.
38     */
39     ptrdiff_t findTargetIdx(IDeformable toFind) {
40         foreach (i, target; targets_) {
41             if (target.target is toFind)
42                 return i;
43         }
44         return -1;
45     }
46 
47 protected:
48 
49     /**
50         Called during the update phase of a new frame.
51         
52         Params:
53             delta =     Time since the last frame.
54             drawList =  The drawlist for the active scene.
55     */
56     override
57     void onUpdate(float delta, DrawList drawList) {
58         auto offset = this.localTransformOffset;
59         foreach (ref BoneTarget target; targets_) {
60             size_t w_length = target.target.deformPoints.length;
61             vec2[] w_verts = target.target.deformPoints;
62 
63             // Resize our temporary deformation buffer if need be.
64             if (boneOffsets.length < w_length)
65                 boneOffsets = boneOffsets.nu_resize(w_length);
66 
67             // Copy over the base bone locations from deformed points.
68             // Then add our delta bone offset transformation to every vertex.
69             // Finally we apply weights based on the weight paint.
70             boneOffsets[0 .. w_length] = w_verts[0 .. w_length];
71             simd_mul(boneOffsets, offset.matrix);
72             simd_mul_weight(boneOffsets, target.weights);
73 
74             // We then add the delta from the bone to the real deformation.
75             target.target.deform(boneOffsets);
76         }
77     }
78 
79 public:
80 
81     /**
82         An immutable slice of the targets of this bone.
83     */
84     @property immutable(BoneTarget)[] targets() => cast(immutable(BoneTarget)[])targets_;
85 
86     /// Destructor
87     ~this() {
88         targets_.clear();
89         nu_freea(boneOffsets);
90     }
91 
92     /**
93         Sets the weights for the given target.
94 
95         Params:
96             target = The target to set weights for.
97             weights = The weights to set.    
98     */
99     void setWeights(IDeformable target, float[] weights) {
100         ptrdiff_t idx = this.findTargetIdx(target);
101         if (idx >= 0) {
102             targets_[idx].weights = weights.nu_dup();
103         }
104     }
105 
106     /**
107         Adds a target
108 
109         Params:
110             target = The target to add.
111     */
112     void addTarget(IDeformable target) {
113         if (this.findTargetIdx(target) == -1) {
114             this.targets_ ~= BoneTarget(target);
115         }
116     }
117 
118     /**
119         Removes a target from this
120 
121         Params:
122             target = The target to remove.
123     */
124     void removeTarget(IDeformable target) {
125         ptrdiff_t idx = this.findTargetIdx(target);
126         if (idx >= 0) {
127             this.targets_.removeAt(idx);
128         }
129     }
130 }
131 
132 mixin Register!(Bone, in_node_registry);
133 // dfmt off
134 
135 
136 
137 
138 /**
139     A target of a bone.
140 */
141 struct BoneTarget {
142 @nogc:
143 
144     /// Destructor.
145      ~this() {
146         nu_freea(weights);
147     }
148 
149     /**
150         The target of the bone.    
151     */
152     IDeformable target;
153 
154     /**
155         Bone weights
156     */
157     float[] weights;
158 }