1 /**
2     Inochi2D Node
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;
14 import inochi2d.core.serde;
15 import inochi2d.core.math;
16 import inochi2d.core.guid;
17 import inochi2d.core;
18 import inochi2d.common;
19 import nulib.string;
20 import numem;
21 import nulib;
22 
23 public import inochi2d.puppet;
24 public import inochi2d.nodes.deformer;
25 public import inochi2d.nodes.visual;
26 public import inochi2d.nodes.legacy;
27 public import inochi2d.nodes.bone;
28 public import inochi2d.core.registry;
29 public import inochi2d.core.property;
30 public import inochi2d.core.render;
31 public import nulib.quark;
32 
33 /**
34     A node in the Inochi2D rendering tree
35 */
36 @TypeId("Node", IN_MAKE_TAG!(0, 0))
37 @RegisterFallback
38 class Node : NuRefCounted, IPropertyOwner, ISerializable, IDeserializable!ModelState {
39 private:
40 @nogc:
41     Puppet puppet_;
42     Node parent_;
43     vector!Node children_;
44     GUID guid_;
45     string nodePath_;
46     uint nid_;
47 
48     // The property store for the node.
49     PropertyStore props_;
50 
51     bool lockToRoot_;
52     Basis globalMatrix_;
53     Basis globalMatrixNoParam_;
54 
55     Transform localTransform_;
56 
57     // Implementation of the transform update algorithm.
58     void transformUpdateImpl() {
59 
60         // Set base matrices.
61         globalMatrix_ = (localTransform_ + localTransformOffset).matrix();
62         globalMatrixNoParam_ = localTransform_.matrix();
63 
64         if (lockToRoot_) {
65             globalMatrix_.matrix = puppet.root.localTransform_.matrix() * globalMatrix_;
66             globalMatrixNoParam_.matrix = puppet.root.localTransform_.matrix() * globalMatrixNoParam_;
67         } else if (parent_ !is null) {
68             globalMatrix_ = parent_.globalMatrix_ * globalMatrix_;
69             globalMatrixNoParam_ = parent_.globalMatrixNoParam_ * globalMatrixNoParam_;
70         }
71     }
72 
73     // Define property list
74     void defineProperties(ref PropertyStore propList) {
75         propList.define!float(PROP_TRANSLATE_X, 0);
76         propList.define!float(PROP_TRANSLATE_Y, 0);
77         propList.define!float(PROP_TRANSLATE_Z, 0);
78         propList.define!float(PROP_ROTATE_X, 0);
79         propList.define!float(PROP_ROTATE_Y, 0);
80         propList.define!float(PROP_ROTATE_Z, 0);
81         propList.define!float(PROP_SCALE_X, 1);
82         propList.define!float(PROP_SCALE_Y, 1);
83 
84         // Overlays that lets us get the values in bulk.
85         propList.defineOverlay!Transform(PROP_TRANSFORM, propList.offsetOf(PROP_TRANSLATE_X));
86 
87         this.onDefineProperties(propList);
88         propList.resetAll();
89     }
90 
91 package(inochi2d):
92 
93     /**
94         Needed for deserialization
95     */
96     void setPuppet(Puppet puppet) @nogc {
97         this.puppet_ = puppet;
98     }
99 
100 protected:
101 
102     /**
103         The Node's numeric ID
104     */
105     final @property uint nid() @nogc pure => nid_;
106 
107     /**
108         Serializes this node to a DataNode.
109 
110         Params:
111             object =    The DataNode to serialize to.
112     */
113     void onSerialize(ref DataNode object) @nogc {
114     }
115 
116     /**
117         Deserializes this node from a DataNode.
118 
119         Params:
120             object =    The DataNode to deserialize from.
121             state =     The state of the deserializer.
122     */
123     void onDeserialize(ref DataNode object, ref ModelState state) @nogc {
124     }
125 
126     /**
127         Called when the node is to finalize its deserialization from disk.
128 
129         Params:
130             state =     The state of the deserializer.
131     */
132     void onFinalize(ref ModelState state) @nogc {
133     }
134 
135     /**
136         Called during the early update phase of a new frame.
137         
138         Params:
139             drawList =  The drawlist for the active scene.
140     */
141     void onPreUpdate(DrawList drawList) @nogc {
142     }
143 
144     /**
145         Called during the update phase of a new frame.
146         
147         Params:
148             delta =     Time since the last frame.
149             drawList =  The drawlist for the active scene.
150     */
151     void onUpdate(float delta, DrawList drawList) @nogc {
152     }
153 
154     /**
155         Called during the late update phase of a new frame.
156         
157         Params:
158             drawList =  The drawlist for the active scene.
159     */
160     void onPostUpdate(DrawList drawList) @nogc {
161     }
162 
163     /**
164         Called when the node is asked to update its transform.
165     */
166     void onTransformUpdate() @nogc {
167     }
168 
169     /**
170         Called when the node is to be redrawn.
171         
172         Params:
173             delta =     Time since the last frame.
174             drawList =  The drawlist for the active scene.
175     */
176     void onDraw(float delta, DrawList drawList) @nogc {
177     }
178 
179     /**
180         Called when the node is moved from one parent
181         to another.
182 
183         Params:
184             from =  The node that used to be this node's parent.
185             to =    The node it was moved to.
186             index = The index the node was moved to.
187     */
188     void onMoved(Node from, Node to, ptrdiff_t index) {
189     }
190 
191     /**
192         Called when the node is to define its properties.
193 
194         Call $(D propList.define) with a quark to do this.
195 
196         Params:
197             propList = The property list to populate.
198     */
199     void onDefineProperties(ref PropertyStore propList) {
200     }
201 
202 public:
203 
204     /**
205         Whether the node is enabled
206     */
207     bool enabled = true;
208 
209     /**
210         Visual name of the node
211     */
212     nstring name = "Unnamed Node";
213 
214     /**
215         The puppet this node is attached to
216     */
217     final @property Puppet puppet() @nogc nothrow pure => parent_ !is null ? parent_.puppet : puppet_;
218 
219     /**
220         The parent of this node
221     */
222     final @property Node parent() @nogc nothrow pure => parent_;
223     final @property void parent(Node node) @nogc {
224         if (node) {
225             node.addChild(this);
226         } else if (parent_) {
227             parent_.removeChild(this);
228         }
229     }
230 
231     /**
232         The index of this node within ints parent.
233     */
234     final @property ptrdiff_t parentIndex() => parent ? parent.findChild(this) : -1;
235 
236     /**
237         Gets a list of this node's children
238     */
239     final @property Node[] children() @nogc nothrow pure => children_;
240 
241     /**
242         The Node's Type ID
243     */
244     final @property TypeId typeId() @nogc => in_node_registry.lookup(this);
245 
246     /**
247         The node's GUID.
248     */
249     @property ref GUID guid() @nogc nothrow pure => guid_;
250 
251     /**
252         The node's property store, should generally not be directly used.
253     */
254     final @property ref PropertyStore props() @nogc nothrow pure => props_;
255 
256     /**
257         The transform in local-space.
258     */
259     @property ref Transform localTransform() @nogc => localTransform_;
260 
261     /**
262         The offset transform in local-space.
263     */
264     @property Transform localTransformOffset() @nogc => props_.get!Transform(PROP_TRANSFORM);
265 
266     /**
267         The global basis matrix.
268     */
269     @property Basis matrix() @nogc => globalMatrix_;
270 
271     /**
272         The global basis matrix with parameter omitted.
273     */
274     @property Basis baseMatrix() @nogc => globalMatrixNoParam_;
275 
276     /**
277         Whether transformation is locked to the root node.
278     */
279     @property bool lockToRoot() @nogc nothrow pure => lockToRoot_;
280     @property void lockToRoot(bool value) @nogc {
281         lockToRoot_ = value;
282     }
283 
284     /**
285         The depth of this node in the node hirearchy
286     */
287     final @property int depth() {
288         int depthV;
289         Node parent = this;
290         while (parent !is null) {
291             depthV++;
292             parent = parent.parent;
293         }
294         return depthV;
295     }
296 
297     /// Destructor
298     ~this() {
299         foreach (child; children_) {
300             child.release();
301         }
302         children_.clear();
303     }
304 
305     /**
306         Constructs a new puppet root node.
307 
308         Params:
309             parent = The puppet this node will belong to.
310     */
311     this(Puppet parent) @nogc {
312         this.defineProperties(props_);
313         this.guid_ = inNewGUID();
314         this.puppet_ = parent;
315     }
316 
317     /**
318         Constructs a new node
319 
320         Params:
321             parent = The node to parent the new node to.
322     */
323     this(Node parent = null) @nogc {
324         this(inNewGUID(), parent);
325     }
326 
327     /**
328         Constructs a new node with an GUID
329 
330         Params:
331             guid =      The GUID to apply to the node.
332             parent =    The node to parent the new node to.
333     */
334     this(GUID guid, Node parent = null) @nogc {
335         this.defineProperties(props_);
336         this.parent = parent;
337         this.guid_ = guid;
338     }
339 
340     /**
341         Removes all children from this node
342     */
343     final void clearChildren() {
344         foreach (child; children_) {
345             child.parent_ = null;
346         }
347         this.children_.clear();
348     }
349 
350     /**
351         Gets whether this node can be moved to the 
352         given target node.
353 
354         Params:
355             to = The move destination.
356 
357         Returns:
358             $(D true) if the node can be moved inside of the given node,
359             $(D false) otherwise.
360     */
361     final bool canMoveTo(Node to) {
362         Node tmp = to;
363         while (tmp !is null) {
364             if (tmp.guid == this.guid)
365                 return false;
366 
367             // Check next up
368             tmp = tmp.parent;
369         }
370         return true;
371     }
372 
373     /**
374         Finds the index of the given direct child node
375         within this node.
376 
377         Params:
378             child = The node to find.
379 
380         Returns:
381             The index of the node,
382             $(D -1) if not found.
383     */
384     final ptrdiff_t findChild(Node child) {
385         return children_.find(child);
386     }
387 
388     /**
389         Removes a given node from this node's children.
390 
391         Params:
392             child = A direct child to remove.
393 
394         Returns:
395             $(D true) if the given node was removed,
396             $(D false) otherwise.
397     */
398     final bool removeChild(Node child) {
399         auto idx = this.findChild(child);
400         if (idx >= 0) {
401             child.onMoved(this, null, -1);
402             child.release();
403 
404             this.children_.removeAt(idx);
405             child.parent_ = null;
406             return true;
407         }
408         return false;
409     }
410 
411     /**
412         Adds a node as a child of this node.
413     */
414     final void addChild(Node child) {
415         child.onMoved(child.parent_, this, this.children_.length);
416         child.retain();
417 
418         // Remove this node from its parent, if needed.
419         if (child.parent_) {
420             child.parent_.removeChild(child);
421         }
422 
423         this.children_ ~= child;
424         child.parent_ = this;
425     }
426 
427     /**
428         Moves the child to the given offset in this
429         node's child list.
430 
431         Params:
432             child =     The child the move
433             to =        The index to move to.
434     */
435     final void moveChild(Node child, ptrdiff_t to) {
436         auto idx = this.findChild(child);
437         if (idx >= 0) {
438             size_t dst = to < 0 ? children_.length - (abs(to) + 1) : to;
439 
440             // Don't swap with itself.
441             if (dst == idx)
442                 return;
443 
444             // Swap if valid.
445             if (dst < this.children_.length) {
446                 child.onMoved(child.parent_, child.parent_, dst);
447                 nu_swap(this.children_[idx], this.children_[to]);
448             }
449         }
450     }
451 
452     /** 
453         Set new Parent
454     */
455     void reparent(Node parent, size_t pOffset) {
456         parent.addChild(this);
457         parent.moveChild(this, pOffset);
458     }
459 
460     /**
461         Serializes this node to a DataNode.
462 
463         Params:
464             recursive = Whether to recurse through children.
465     */
466     final DataNode serialize(bool recursive = true) @nogc {
467         auto result = DataNode.createObject();
468         this.serialize(result, recursive);
469         return result;
470     }
471 
472     /**
473         Serializes this node to a DataNode.
474 
475         Params:
476             object =    The DataNode to serialize to.
477             recursive = Whether to recurse through children.
478     */
479     final void serialize(ref DataNode object, bool recursive = true) @nogc {
480         nstring guid = guid_.toString();
481         object["guid"] = guid[];
482         object["name"] = name[];
483         object["type"] = typeId.sid;
484         object["enabled"] = enabled;
485         object["transform"] = localTransform_.serialize();
486         object["lockToRoot"] = lockToRoot_;
487 
488         // Call callback and iterate to children.
489         this.onSerialize(object);
490 
491         // Recurse through children if enabled.
492         if (recursive) {
493             object["children"] = DataNode.createArray();
494             foreach (child; children) {
495                 auto childObject = DataNode.createObject();
496                 child.serialize(childObject);
497 
498                 object["children"] ~= childObject;
499             }
500         }
501     }
502 
503     /**
504         Deserializes this node from a DataNode.
505 
506         Params:
507             object =    The DataNode to deserialize from.
508             state =     The state of the deserializer.
509     */
510     final void deserialize(ref DataNode object, ref ModelState state) @nogc {
511         this.guid_ = object.tryGetGUID(state, "uuid", "guid");
512         object.tryGetRef(state, enabled, "enabled");
513         object.tryGetRef(state, name, "name");
514         object.tryGetRef(state, localTransform_, "transform");
515         object.tryGetRef(state, lockToRoot_, "lockToRoot");
516 
517         // This ugly hack exists to upgrade legacy masks to the new masks.
518         float zsort08 = 0;
519         if (state.doUpgrade08) {
520             if ("zsort" !in state.upctx)
521                 state.upctx["zsort"] = zsort08;
522 
523             object.tryGetRef(state, zsort08, "zsort");
524             zsort08 += state.upctx["zsort"];
525             state.upctx["zsort"] = zsort08;
526         }
527 
528         // Call callback and iterate to children.
529         this.onDeserialize(object, state);
530 
531         // Pre-populate our children with the correct types
532         if ("children" in object && object["children"].isArray) {
533             foreach (ref child; object["children"].array) {
534 
535                 // This ugly hack exists to upgrade legacy masks to the new masks.
536                 if (state.doUpgrade08) {
537                     state.upctx["zsort"] = zsort08;
538                 }
539 
540                 // NOTE:    inInstantiateNode implicitly handles setting the
541                 //          Parent-child relationship, so we don't need to do
542                 //          anything else besides pass it onto the child's
543                 //          deserializer.
544                 if (Node n = in_node_registry.tryCreateFrom(child)) {
545                     n.parent = this;
546                     n.deserialize(child, state);
547                 }
548             }
549         }
550     }
551 
552     /**
553         Finalizes this node and its children.
554 
555         Params:
556             state =     The state of the deserializer.
557     */
558     final void finalize(ref ModelState state) @nogc {
559         nid_ = typeId.nid;
560 
561         // Call callback and iterate to children.
562         this.onFinalize(state);
563         foreach (child; this.children_) {
564             child.finalize(state);
565         }
566     }
567 
568     /**
569         Updates the transform of the node and all nodes underneath it.
570 
571         Note:
572             Children which have been disabled will not be updated.
573     */
574     final void updateTransform() @nogc {
575 
576         // Do the base algorithm first,
577         // then pass on to callback and iterate to children.
578         this.transformUpdateImpl();
579         this.onTransformUpdate();
580 
581         foreach (child; children_) {
582             child.updateTransform();
583         }
584     }
585 
586     /**
587         Runs a pre-update cycle for this node and its enabled children.
588 
589         Params:
590             drawList =  The drawlist for the active scene.
591 
592         Note:
593             This is generally called by the puppet and shouldn't be called
594             by you outside of circumstances where the puppet isn't
595             controlling rendering.
596     */
597     final void preUpdate(DrawList drawList) @nogc {
598         if (!enabled)
599             return;
600 
601         this.onPreUpdate(drawList);
602         foreach (child; children_) {
603             child.preUpdate(drawList);
604         }
605     }
606 
607     /**
608         Updates the node
609 
610         Params:
611             delta =     Time since the last frame.
612             drawList =  The drawlist for the active scene.
613 
614         Note:
615             This is generally called by the puppet and shouldn't be called
616             by you outside of circumstances where the puppet isn't
617             controlling rendering.
618     */
619     final void update(float delta, DrawList drawList) @nogc {
620         if (!enabled)
621             return;
622 
623         this.onUpdate(delta, drawList);
624         foreach (child; children) {
625             child.update(delta, drawList);
626         }
627     }
628 
629     /**
630         Update sequence run after the main update sequence.
631 
632         Params:
633             drawList =  The drawlist for the active scene.
634 
635         Note:
636             This is generally called by the puppet and shouldn't be called
637             by you outside of circumstances where the puppet isn't
638             controlling rendering.
639     */
640     final void postUpdate(DrawList drawList) @nogc {
641         if (!enabled)
642             return;
643 
644         this.onPostUpdate(drawList);
645         foreach (child; children_) {
646             child.postUpdate(drawList);
647         }
648     }
649 
650     /**
651         Draws this node and it's subnodes
652         
653         Params:
654             delta =     Time since the last frame.
655             drawList =  The drawlist for the active scene.
656 
657         Note:
658             This is generally called by the puppet and shouldn't be called
659             by you outside of circumstances where the puppet isn't
660             controlling rendering.
661     */
662     final void draw(float delta, DrawList drawList) @nogc {
663         this.onDraw(delta, drawList);
664     }
665 
666     /**
667         Gets whether a property with the given name exists
668         in the object.
669 
670         Params:
671             key = The name of the property.
672         
673         Returns:
674             $(D true) if the property exists,
675             $(D false) otherwise.
676     */
677     bool hasProperty(quark key) const @nogc nothrow {
678         return props_.offsetOf(key) != -1;
679     }
680 
681     /**
682         Gets the value of a given property.
683 
684         Params:
685             key = The name of the property.
686         
687         Returns:
688             The floating point value of the property.
689     */
690     float getProperty(quark key) const @nogc nothrow {
691         return props_.get!float(key);
692     }
693 
694     /**
695         Gets the default value of a given property.
696 
697         Params:
698             key = The name of the property.
699         
700         Returns:
701             The default value of the property.
702     */
703     float getPropertyDefault(quark key) const @nogc nothrow {
704         return props_.getDefault!float(key);
705     }
706 
707     /**
708         Sets the value of the property.
709 
710         Params:
711             key =   The name of the property.
712             value = The value to set the property to.
713     */
714     void setProperty(quark key, float value) @nogc nothrow {
715         return props_.set!float(key, value);
716     }
717 
718     /**
719         Resets the given property.
720 
721         Params:
722             key = The name of the property.
723     */
724     void resetProperty(quark key) @nogc nothrow {
725         props_.reset(key);
726     }
727 
728     /**
729         Resets all properties.
730     */
731     void resetProperties() @nogc nothrow {
732         props_.resetAll();
733     }
734 
735     /**
736         Gets the string representation of this object.
737     */
738     override
739     string toString() const {
740         return name[];
741     }
742 }
743 
744 mixin Register!(Node, in_node_registry);
745 // dfmt off
746 
747 
748 
749 
750 //
751 //          TYPES AND REGISTRIES
752 //
753 
754 /**
755     The public node registry.
756 */
757 __gshared TypeRegistry!Node in_node_registry;
758 
759 
760 
761 
762 //
763 //          QUARKS
764 //
765 
766 // Register quarks for this file.
767 mixin RegisterQuarks!();
768 
769 /**
770     A transform
771 */
772 @propkey("transform")
773 __gshared immutable(quark) PROP_TRANSFORM;
774 
775 /**
776     X translation.
777 */
778 @propkey("transform.t.x")
779 __gshared immutable(quark) PROP_TRANSLATE_X;
780 
781 /**
782     Y translation.
783 */
784 @propkey("transform.t.y")
785 __gshared immutable(quark) PROP_TRANSLATE_Y;
786 
787 /**
788     Z translation.
789 */
790 @propkey("transform.t.z")
791 __gshared immutable(quark) PROP_TRANSLATE_Z;
792 
793 /**
794     X rotation.
795 */
796 @propkey("transform.r.x")
797 __gshared immutable(quark) PROP_ROTATE_X;
798 
799 /**
800     y rotation.
801 */
802 @propkey("transform.r.y")
803 __gshared immutable(quark) PROP_ROTATE_Y;
804 
805 /**
806     z rotation.
807 */
808 @propkey("transform.r.z")
809 __gshared immutable(quark) PROP_ROTATE_Z;
810 
811 /**
812     X scale.
813 */
814 @propkey("transform.s.x")
815 __gshared immutable(quark) PROP_SCALE_X;
816 
817 /**
818     y scale.
819 */
820 @propkey("transform.s.y")
821 __gshared immutable(quark) PROP_SCALE_Y;
822 
823 
824 
825 
826 //
827 //          HELPER FUNCTIONS
828 //
829 
830 /**
831     Finds all nodes of the given type (and subtypes) in the node tree.
832 
833     Params:
834         root =  The root node to start searching from.
835         list =  The list to write the results to.
836 */
837 void findNodes(T)(Node root, ref T[] list) @nogc if (is(T : Node)) {
838     static void findNodesImpl(Node node, ref T[] list) @nogc {
839         if (!node)
840             return;
841 
842         if (auto found = cast(T)node) {
843             list = list.nu_resize(list.length + 1);
844             list[$ - 1] = found;
845         }
846 
847         // Non-part nodes just need to be recursed through,
848         // they don't draw anything.
849         foreach (child; node.children) {
850             findNodesImpl(child, list);
851         }
852     }
853 
854     nu_cleara(list);
855     findNodesImpl(root, list);
856 }
857 
858 /**
859     Sorts a slice of visuals in-place.
860 
861     Params:
862         slice = The slice to sort.
863 */
864 void sortNodes(T)(T[] slice) @nogc nothrow if (is(T : Node)) {
865     import nulib.math.fixed : fixed32;
866     import numem.sorting : nu_sort;
867 
868     // HACK:    nulib doesn't have a float cmp function yet,
869     //          as such we convert sorting values to fixed.
870     nu_sort!((a, b) @nogc => a.zSortRender > b.zSortRender)(slice);
871 }