1 module inochi2d.core.puppet;
2 import inochi2d.fmt.serialize;
3 import inochi2d.core;
4 import inochi2d.math;
5 import std.algorithm.sorting;
6 import std.algorithm.mutation : SwapStrategy;
7 import std.exception;
8 import std.format;
9 import std.file;
10 import std.path : extension;
11 import std.json;
12 
13 /**
14     Magic value meaning that the model has no thumbnail
15 */
16 enum NO_THUMBNAIL = uint.max;
17 
18 enum PuppetAllowedUsers {
19     /**
20         Only the author(s) are allowed to use the puppet
21     */
22     OnlyAuthor = "onlyAuthor",
23 
24     /**
25         Only licensee(s) are allowed to use the puppet
26     */
27     OnlyLicensee = "onlyLicensee",
28 
29     /**
30         Everyone may use the model
31     */
32     Everyone = "everyone"
33 }
34 
35 enum PuppetAllowedRedistribution {
36     /**
37         Redistribution is prohibited
38     */
39     Prohibited = "prohibited",
40 
41     /**
42         Redistribution is allowed, but only under
43         the same license as the original.
44     */
45     ViralLicense = "viralLicense",
46 
47     /**
48         Redistribution is allowed, and the puppet
49         may be redistributed under a different
50         license than the original.
51 
52         This goes in conjunction with modification rights.
53     */
54     CopyleftLicense = "copyleftLicense"
55 }
56 
57 enum PuppetAllowedModification {
58     /**
59         Modification is prohibited
60     */
61     Prohibited = "prohibited",
62 
63     /**
64         Modification is only allowed for personal use
65     */
66     AllowPersonal = "allowPersonal",
67 
68     /**
69         Modification is allowed with redistribution,
70         see allowedRedistribution for redistribution terms.
71     */
72     AllowRedistribute = "allowRedistribute",
73 }
74 
75 class PuppetUsageRights {
76     /**
77         Who is allowed to use the puppet?
78     */
79     @Optional
80     PuppetAllowedUsers allowedUsers = PuppetAllowedUsers.OnlyAuthor;
81 
82     /**
83         Whether violence content is allowed
84     */
85     @Optional
86     bool allowViolence = false;
87 
88     /**
89         Whether sexual content is allowed
90     */
91     @Optional
92     bool allowSexual = false;
93 
94     /**
95         Whether commerical use is allowed
96     */
97     @Optional
98     bool allowCommercial = false;
99 
100     /**
101         Whether a model may be redistributed
102     */
103     @Optional
104     PuppetAllowedRedistribution allowRedistribution = PuppetAllowedRedistribution.Prohibited;
105 
106     /**
107         Whether a model may be modified
108     */
109     @Optional
110     PuppetAllowedModification allowModification = PuppetAllowedModification.Prohibited;
111 
112     /**
113         Whether the author(s) must be attributed for use.
114     */
115     @Optional
116     bool requireAttribution = false;
117 }
118 
119 /**
120     Puppet meta information
121 */
122 class PuppetMeta {
123 
124     /**
125         Name of the puppet
126     */
127     string name;
128     /**
129         Version of the Inochi2D spec that was used for creating this model
130     */
131     @Name("version")
132     string version_ = "1.0-alpha";
133 
134     /**
135         Rigger(s) of the puppet
136     */
137     @Optional
138     string rigger;
139 
140     /**
141         Artist(s) of the puppet
142     */
143     @Optional
144     string artist;
145 
146     /**
147         Usage Rights of the puppet
148     */
149     @Optional
150     PuppetUsageRights rights;
151 
152     /**
153         Copyright string
154     */
155     @Optional
156     string copyright;
157 
158     /**
159         URL of license
160     */
161     @Optional
162     string licenseURL;
163 
164     /**
165         Contact information of the first author
166     */
167     @Optional
168     string contact;
169 
170     /**
171         Link to the origin of this puppet
172     */
173     @Optional
174     string reference;
175 
176     /**
177         Texture ID of this puppet's thumbnail
178     */
179     @Optional
180     uint thumbnailId = NO_THUMBNAIL;
181 
182     /**
183         Whether the puppet should preserve pixel borders.
184         This feature is mainly useful for puppets which use pixel art.
185     */
186     @Optional
187     bool preservePixels = false;
188 }
189 
190 /**
191     Puppet physics settings
192 */
193 class PuppetPhysics {
194     @Optional
195     float pixelsPerMeter = 1000;
196 
197     @Optional
198     float gravity = 9.8;
199 }
200 
201 /**
202     A puppet
203 */
204 class Puppet {
205 private:
206     /**
207         An internal puppet root node
208     */
209     @Ignore
210     Node puppetRootNode;
211 
212     /**
213         A list of parts that are not masked by other parts
214 
215         for Z sorting
216     */
217     @Ignore
218     Node[] rootParts;
219 
220     /**
221         A list of drivers that need to run to update the puppet
222     */
223     Driver[] drivers;
224 
225     /**
226         A list of parameters that are driven by drivers
227     */
228     Driver[Parameter] drivenParameters;
229 
230     /**
231         A dictionary of named animations
232     */
233     Animation[string] animations;
234 
235     void scanPartsRecurse(ref Node node, bool driversOnly = false) {
236 
237         // Don't need to scan null nodes
238         if (node is null) return;
239 
240         // Collect Drivers
241         if (Driver part = cast(Driver)node) {
242             drivers ~= part;
243             foreach(Parameter param; part.getAffectedParameters())
244                 drivenParameters[param] = part;
245         } else if (!driversOnly) {
246             // Collect drawable nodes only if we aren't inside a Composite node
247 
248             if (Composite composite = cast(Composite)node) {
249                 // Composite nodes handle and keep their own root node list, as such we should just draw them directly
250                 composite.scanParts();
251                 rootParts ~= composite;
252 
253                 // For this subtree, only look for Drivers
254                 driversOnly = true;
255             } else if (Part part = cast(Part)node) {
256                 // Collect Part nodes
257                 rootParts ~= part;
258             }
259             // Non-part nodes just need to be recursed through,
260             // they don't draw anything.
261         }
262 
263         // Recurse through children nodes
264         foreach(child; node.children) {
265             scanPartsRecurse(child, driversOnly);
266         }
267     }
268 
269     final void scanParts(bool reparent = false)(ref Node node) {
270 
271         // We want rootParts to be cleared so that we
272         // don't draw the same part multiple times
273         // and if the node tree changed we want to reflect those changes
274         // not the old node tree.
275         rootParts = [];
276 
277         // Same for drivers
278         drivers = [];
279         drivenParameters.clear();
280 
281         this.scanPartsRecurse(node);
282 
283         // To make sure the GC can collect any nodes that aren't referenced
284         // anymore, we clear its children first, then assign its new child
285         // to our "new" root node. In some cases the root node will be
286         // quite different.
287         static if (reparent) { 
288             if (puppetRootNode !is null) puppetRootNode.clearChildren();
289             node.parent = puppetRootNode;
290         }
291     }
292 
293     void selfSort() {
294         import std.math : cmp;
295         sort!((a, b) => cmp(
296             a.zSort, 
297             b.zSort) > 0, SwapStrategy.stable)(rootParts);
298     }
299 
300     Node findNode(Node n, string name) {
301 
302         // Name matches!
303         if (n.name == name) return n;
304 
305         // Recurse through children
306         foreach(child; n.children) {
307             if (Node c = findNode(child, name)) return c;
308         }
309 
310         // Not found
311         return null;
312     }
313 
314     Node findNode(Node n, uint uuid) {
315 
316         // Name matches!
317         if (n.uuid == uuid) return n;
318 
319         // Recurse through children
320         foreach(child; n.children) {
321             if (Node c = findNode(child, uuid)) return c;
322         }
323 
324         // Not found
325         return null;
326     }
327 
328 public:
329     /**
330         Meta information about this puppet
331     */
332     @Name("meta")
333     PuppetMeta meta;
334 
335     /**
336         Global physics settings for this puppet
337     */
338     @Name("physics")
339     PuppetPhysics physics;
340 
341     /**
342         The root node of the puppet
343     */
344     @Name("nodes", "Root Node")
345     Node root;
346 
347     /**
348         Parameters
349     */
350     @Name("param", "Parameters")
351     @Optional
352     Parameter[] parameters;
353 
354     /**
355         Parameters
356     */
357     @Name("automation", "Automation")
358     @Optional
359     Automation[] automation;
360 
361     /**
362         INP Texture slots for this puppet
363     */
364     @Ignore
365     Texture[] textureSlots;
366 
367     /**
368         Extended vendor data
369     */
370     @Ignore
371     ubyte[][string] extData;
372 
373     /**
374         Whether parameters should be rendered
375     */
376     @Ignore
377     bool renderParameters = true;
378 
379     /**
380         Whether drivers should run
381     */
382     @Ignore
383     bool enableDrivers = true;
384 
385     /**
386         Puppet render transform
387 
388         This transform does not affect physics
389     */
390     Transform transform;
391 
392     /**
393         Creates a new puppet from nothing ()
394     */
395     this() { 
396         this.puppetRootNode = new Node(this); 
397         this.meta = new PuppetMeta();
398         this.physics = new PuppetPhysics();
399         root = new Node(this.puppetRootNode); 
400         root.name = "Root";
401         transform = Transform(vec3(0, 0, 0));
402     }
403 
404     /**
405         Creates a new puppet from a node tree
406     */
407     this(Node root) {
408         this.meta = new PuppetMeta();
409         this.physics = new PuppetPhysics();
410         this.root = root;
411         this.puppetRootNode = new Node(this);
412         this.root.name = "Root";
413         this.scanParts!true(this.root);
414         transform = Transform(vec3(0, 0, 0));
415         this.selfSort();
416     }
417 
418     /**
419         Updates the nodes
420     */
421     final void update() {
422         transform.update();
423 
424         // Update Automators
425         foreach(auto_; automation) {
426             auto_.update();
427         }
428 
429         root.beginUpdate();
430 
431         if (renderParameters) {
432 
433             // Update parameters
434             foreach(parameter; parameters) {
435 
436                 if (!enableDrivers || parameter !in drivenParameters)
437                     parameter.update();
438             }
439         }
440 
441         // Ensure the transform tree is updated
442         root.transformChanged();
443 
444         if (renderParameters && enableDrivers) {
445             // Update parameter/node driver nodes (e.g. physics)
446             foreach(driver; drivers) {
447                 driver.updateDriver();
448             }
449         }
450 
451         // Update nodes
452         root.update();
453     }
454 
455     /**
456         Reset drivers/physics nodes
457     */
458     final void resetDrivers() {
459         foreach(driver; drivers) {
460             driver.reset();
461         }
462 
463         // Update so that the timestep gets reset.
464         import inochi2d : inUpdate;
465         inUpdate();
466     }
467 
468     /**
469         Returns the index of a parameter by name
470     */
471     ptrdiff_t findParameterIndex(string name) {
472         foreach(i, parameter; parameters) {
473             if (parameter.name == name) {
474                 return i;
475             }
476         }
477         return -1;
478     }
479 
480     /**
481         Returns a parameter by UUID
482     */
483     Parameter findParameter(uint uuid) {
484         foreach(i, parameter; parameters) {
485             if (parameter.uuid == uuid) {
486                 return parameter;
487             }
488         }
489         return null;
490     }
491 
492     /**
493         Gets if a node is bound to ANY parameter.
494     */
495     bool getIsNodeBound(Node n) {
496         foreach(i, parameter; parameters) {
497             if (parameter.hasAnyBinding(n)) return true;
498         }
499         return false;
500     }
501 
502     /**
503         Draws the puppet
504     */
505     final void draw() {
506         this.selfSort();
507 
508         foreach(rootPart; rootParts) {
509             if (!rootPart.renderEnabled) continue;
510             rootPart.drawOne();
511         }
512     }
513 
514     /**
515         Removes a parameter from this puppet
516     */
517     void removeParameter(Parameter param) {
518         import std.algorithm.searching : countUntil;
519         import std.algorithm.mutation : remove;
520         ptrdiff_t idx = parameters.countUntil(param);
521         if (idx >= 0) {
522             parameters = parameters.remove(idx);
523         }
524     }
525 
526     /**
527         Rescans the puppet's nodes
528 
529         Run this every time you change the layout of the puppet's node tree
530     */
531     final void rescanNodes() {
532         this.scanParts!false(root);
533     }
534 
535     /**
536         Updates the texture state for all texture slots.
537     */
538     final void updateTextureState() {
539 
540         // Update filtering mode for texture slots
541         foreach(texutre; textureSlots) {
542             texutre.setFiltering(meta.preservePixels ? Filtering.Point : Filtering.Linear);
543         }
544     }
545 
546     /**
547         Finds Node by its name
548     */
549     T find(T = Node)(string name) if (is(T : Node)) {
550         return cast(T)findNode(root, name);
551     }
552 
553     /**
554         Finds Node by its unique id
555     */
556     T find(T = Node)(uint uuid) if (is(T : Node)) {
557         return cast(T)findNode(root, uuid);
558     }
559 
560     /**
561         Returns all the parts in the puppet
562     */
563     Part[] getAllParts() {
564         return findNodesType!Part(root);
565     }
566 
567     /**
568         Finds nodes based on their type
569     */
570     final T[] findNodesType(T)(Node n) if (is(T : Node)) {
571         T[] nodes;
572 
573         if (T item = cast(T)n) {
574             nodes ~= item;
575         }
576 
577         // Recurse through children
578         foreach(child; n.children) {
579             nodes ~= findNodesType!T(child);
580         }
581 
582         return nodes;
583     }
584 
585     /**
586         Adds a texture to a new slot if it doesn't already exist within this puppet
587     */
588     final uint addTextureToSlot(Texture texture) {
589         import std.algorithm.searching : canFind;
590 
591         // Add texture if we can't find it.
592         if (!textureSlots.canFind(texture)) textureSlots ~= texture;
593         return cast(uint)textureSlots.length-1;
594     }
595 
596     /**
597         Populate texture slots with all visible textures in the model
598     */
599     final void populateTextureSlots() {
600         if (textureSlots.length > 0) textureSlots.length = 0;
601         
602         foreach(part; getAllParts) {
603             foreach(texture; part.textures) {
604                 if (texture) this.addTextureToSlot(texture);
605             }
606         }
607     }
608 
609     /**
610         Finds a texture by its runtime UUID
611     */
612     final Texture findTextureByRuntimeUUID(uint uuid) {
613         foreach(ref slot; textureSlots) {
614             if (slot.getRuntimeUUID())
615                 return slot;
616         }
617         return null;
618     }
619 
620     /**
621         Sets thumbnail of this puppet
622     */
623     final void setThumbnail(Texture texture) {
624         if (this.meta.thumbnailId == NO_THUMBNAIL) {
625             this.meta.thumbnailId = this.addTextureToSlot(texture);
626         } else {
627             textureSlots[this.meta.thumbnailId] = texture;
628         }
629     }
630 
631     /**
632         Gets the texture slot index for a texture
633 
634         returns -1 if none was found
635     */
636     final ptrdiff_t getTextureSlotIndexFor(Texture texture) {
637         import std.algorithm.searching : countUntil;
638         return textureSlots.countUntil(texture);
639     }
640 
641     /**
642         Clears this puppet's thumbnail
643 
644         By default it does not delete the texture assigned, pass in true to delete texture
645     */
646     final void clearThumbnail(bool deleteTexture = false) {
647         import std.algorithm.mutation : remove;
648         if (deleteTexture) textureSlots = remove(textureSlots, this.meta.thumbnailId);
649         this.meta.thumbnailId = NO_THUMBNAIL;
650     }
651 
652     /**
653         This cursed toString implementation outputs the puppet's
654         nodetree as a pretty printed tree.
655 
656         Please use a graphical viewer instead of this if you can,
657         eg. Inochi Creator.
658     */
659     override
660     string toString() {
661         import std.format : format;
662         import std.range : repeat, takeExactly;
663         import std.array : array;
664         bool[] lineSet;
665 
666         string toStringBranch(Node n, int indent, bool showLines = true) {
667 
668             lineSet ~= n.children.length > 0;
669             string getLineSet() {
670                 if (indent == 0) return "";
671                 string s = "";
672                 foreach(i; 1..lineSet.length) {
673                     s ~= lineSet[i-1] ? "│ " : "  ";
674                 }
675                 return s;
676             }
677 
678             string iden = getLineSet();
679 
680             string s = "%s[%s] %s <%s>\n".format(n.children.length > 0 ? "╭─" : "", n.typeId, n.name, n.uuid);
681             foreach(i, child; n.children) {
682                 string term = "├→";
683                 if (i == n.children.length-1) {
684                     term = "╰→";
685                     lineSet[indent] = false;
686                 }
687                 s ~= "%s%s%s".format(iden, term, toStringBranch(child, indent+1));
688             }
689 
690             lineSet.length--;
691 
692             return s;
693         }
694 
695         return toStringBranch(root, 0);
696     }
697 
698 
699     void serializeSelf(ref InochiSerializer serializer) {
700         serializer.putKey("meta");
701         serializer.serializeValue(meta);
702         serializer.putKey("physics");
703         serializer.serializeValue(physics);
704         serializer.putKey("nodes");
705         serializer.serializeValue(root);
706         serializer.putKey("param");
707         serializer.serializeValue(parameters);
708         serializer.putKey("automation");
709         serializer.serializeValue(automation);
710         serializer.putKey("animations");
711         serializer.serializeValue(animations);
712     }
713 
714     /**
715         Serializes a puppet
716     */
717     void serialize(ref InochiSerializer serializer) {
718         auto state = serializer.objectBegin;
719         serializeSelf(serializer);
720         serializer.objectEnd(state);
721     }
722 
723     /**
724         Deserializes a puppet
725     */
726     SerdeException deserializeFromFghj(Fghj data) {
727         if (auto exc = data["meta"].deserializeValue(this.meta)) return exc;
728         if (!data["physics"].isEmpty)
729             if (auto exc = data["physics"].deserializeValue(this.physics)) return exc;
730         if (auto exc = data["nodes"].deserializeValue(this.root)) return exc;
731 
732         // Allow parameter loading to be overridden (for Inochi Creator)
733         foreach(key; data["param"].byElement) {
734             this.parameters ~= inParameterCreate(key);
735         }
736 
737         // Deserialize automation
738         foreach(key; data["automation"].byElement) {
739             string type;
740             if (auto exc = key["type"].deserializeValue(type)) return exc;
741 
742             if (inHasAutomationType(type)) {
743                 auto auto_ = inInstantiateAutomation(type, this);
744                 auto_.deserializeFromFghj(key);
745                 this.automation ~= auto_;
746             }
747         }
748         if (!data["animations"].isEmpty) data["animations"].deserializeValue(animations);
749         this.finalizeDeserialization(data);
750 
751         return null;
752     }
753 
754 
755     void reconstruct() {
756         this.root.reconstruct();
757         foreach(parameter; parameters.dup) {
758             parameter.reconstruct(this);
759         }
760         foreach(automation_; automation.dup) {
761             automation_.reconstruct(this);
762         }
763         foreach(ref animation; animations.dup) {
764             animation.reconstruct(this);
765         }
766     }
767 
768     void finalize() {
769         this.root.setPuppet(this);
770         this.root.name = "Root";
771         this.puppetRootNode = new Node(this);
772 
773         // Finally update link etc.
774         this.root.finalize();
775         foreach(parameter; parameters) {
776             parameter.finalize(this);
777         }
778         foreach(automation_; automation) {
779             automation_.finalize(this);
780         }
781         foreach(ref animation; animations) {
782             animation.finalize(this);
783         }
784         this.scanParts!true(this.root);
785         this.selfSort();
786     }
787     /**
788         Finalizer
789     */
790     void finalizeDeserialization(Fghj data) {
791         // reconstruct object path so that object is located at final position
792         reconstruct();
793         finalize();
794     }
795 
796     /**
797         Gets the internal root parts array 
798 
799         Do note that some root parts may be Composites instead.
800     */
801     final 
802     ref Node[] getRootParts() {
803         return rootParts;
804     }
805 
806     /**
807         Gets a list of drivers
808     */
809     final
810     ref Driver[] getDrivers() {
811         return drivers;
812     }
813 
814    /**
815         Gets a mapping from parameters to their drivers
816     */
817     final
818     ref Driver[Parameter] getParameterDrivers() {
819         return drivenParameters;
820     }
821 
822     /**
823         Gets the animation dictionary
824     */
825     final
826     ref Animation[string] getAnimations() {
827         return animations;
828     }
829 
830     void applyDeformToChildren() {
831         auto nodes = findNodesType!MeshGroup(root);
832         foreach (node; nodes) {
833             node.applyDeformToChildren(parameters);
834         }
835     }
836 
837     /**
838         Gets the combined bounds of the puppet
839     */
840     vec4 getCombinedBounds(bool reupdate=false)() {
841         return root.getCombinedBounds!(reupdate, true);
842     }
843 }