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     void scanPartsRecurse(ref Node node, bool driversOnly = false) {
231 
232         // Don't need to scan null nodes
233         if (node is null) return;
234 
235         // Collect Drivers
236         if (Driver part = cast(Driver)node) {
237             drivers ~= part;
238             foreach(Parameter param; part.getAffectedParameters())
239                 drivenParameters[param] = part;
240         } else if (!driversOnly) {
241             // Collect drawable nodes only if we aren't inside a Composite node
242 
243             if (Composite composite = cast(Composite)node) {
244                 // Composite nodes handle and keep their own root node list, as such we should just draw them directly
245                 composite.scanParts();
246                 rootParts ~= composite;
247 
248                 // For this subtree, only look for Drivers
249                 driversOnly = true;
250             } else if (Part part = cast(Part)node) {
251                 // Collect Part nodes
252                 rootParts ~= part;
253             }
254             // Non-part nodes just need to be recursed through,
255             // they don't draw anything.
256         }
257 
258         // Recurse through children nodes
259         foreach(child; node.children) {
260             scanPartsRecurse(child, driversOnly);
261         }
262     }
263 
264     final void scanParts(bool reparent = false)(ref Node node) {
265 
266         // We want rootParts to be cleared so that we
267         // don't draw the same part multiple times
268         // and if the node tree changed we want to reflect those changes
269         // not the old node tree.
270         rootParts = [];
271 
272         // Same for drivers
273         drivers = [];
274         drivenParameters.clear();
275 
276         this.scanPartsRecurse(node);
277 
278         // To make sure the GC can collect any nodes that aren't referenced
279         // anymore, we clear its children first, then assign its new child
280         // to our "new" root node. In some cases the root node will be
281         // quite different.
282         static if (reparent) { 
283             if (puppetRootNode !is null) puppetRootNode.clearChildren();
284             node.parent = puppetRootNode;
285         }
286     }
287 
288     void selfSort() {
289         import std.math : cmp;
290         sort!((a, b) => cmp(
291             a.zSort, 
292             b.zSort) > 0, SwapStrategy.stable)(rootParts);
293     }
294 
295     Node findNode(Node n, string name) {
296 
297         // Name matches!
298         if (n.name == name) return n;
299 
300         // Recurse through children
301         foreach(child; n.children) {
302             if (Node c = findNode(child, name)) return c;
303         }
304 
305         // Not found
306         return null;
307     }
308 
309     Node findNode(Node n, uint uuid) {
310 
311         // Name matches!
312         if (n.uuid == uuid) return n;
313 
314         // Recurse through children
315         foreach(child; n.children) {
316             if (Node c = findNode(child, uuid)) return c;
317         }
318 
319         // Not found
320         return null;
321     }
322 
323 public:
324     /**
325         Meta information about this puppet
326     */
327     @Name("meta")
328     PuppetMeta meta;
329 
330     /**
331         Global physics settings for this puppet
332     */
333     @Name("physics")
334     PuppetPhysics physics;
335 
336     /**
337         The root node of the puppet
338     */
339     @Name("nodes", "Root Node")
340     Node root;
341 
342     /**
343         Parameters
344     */
345     @Name("param", "Parameters")
346     @Optional
347     Parameter[] parameters;
348 
349     /**
350         Parameters
351     */
352     @Name("automation", "Automation")
353     @Optional
354     Automation[] automation;
355 
356     /**
357         INP Texture slots for this puppet
358     */
359     @Ignore
360     Texture[] textureSlots;
361 
362     /**
363         Extended vendor data
364     */
365     @Ignore
366     ubyte[][string] extData;
367 
368     /**
369         Whether parameters should be rendered
370     */
371     @Ignore
372     bool renderParameters = true;
373 
374     /**
375         Whether drivers should run
376     */
377     @Ignore
378     bool enableDrivers = true;
379 
380     /**
381         Creates a new puppet from nothing ()
382     */
383     this() { 
384         this.puppetRootNode = new Node(this); 
385         this.meta = new PuppetMeta();
386         this.physics = new PuppetPhysics();
387         root = new Node(this.puppetRootNode); 
388         root.name = "Root";
389     }
390 
391     /**
392         Creates a new puppet from a node tree
393     */
394     this(Node root) {
395         this.meta = new PuppetMeta();
396         this.physics = new PuppetPhysics();
397         this.root = root;
398         this.puppetRootNode = new Node(this);
399         this.root.name = "Root";
400         this.scanParts!true(this.root);
401         this.selfSort();
402     }
403 
404     /**
405         Updates the nodes
406     */
407     final void update() {
408 
409         // Rest additive offsets
410         foreach(parameter; parameters) {
411             parameter.preUpdate();
412         }
413 
414         // Update Automators
415         foreach(auto_; this.automation) {
416             auto_.update();
417         }
418 
419         root.beginUpdate();
420 
421         if (renderParameters) {
422 
423             // Update parameters
424             foreach(parameter; parameters) {
425                 if (!enableDrivers || parameter !in drivenParameters)
426                     parameter.update();
427             }
428         }
429 
430         // Ensure the transform tree is updated
431         root.transformChanged();
432 
433         if (renderParameters && enableDrivers) {
434             // Update parameter/node driver nodes (e.g. physics)
435             foreach(driver; drivers) {
436                 driver.updateDriver();
437             }
438         }
439 
440         // Update nodes
441         root.update();
442     }
443 
444     /**
445         Reset drivers/physics nodes
446     */
447     final void resetDrivers() {
448         foreach(driver; drivers) {
449             driver.reset();
450         }
451     }
452 
453     /**
454         Returns the index of a parameter by name
455     */
456     ptrdiff_t findParameterIndex(string name) {
457         foreach(i, parameter; parameters) {
458             if (parameter.name == name) {
459                 return i;
460             }
461         }
462         return -1;
463     }
464 
465     /**
466         Returns a parameter by UUID
467     */
468     Parameter findParameter(uint uuid) {
469         foreach(i, parameter; parameters) {
470             if (parameter.uuid == uuid) {
471                 return parameter;
472             }
473         }
474         return null;
475     }
476 
477     /**
478         Gets if a node is bound to ANY parameter.
479     */
480     bool getIsNodeBound(Node n) {
481         foreach(i, parameter; parameters) {
482             if (parameter.hasAnyBinding(n)) return true;
483         }
484         return false;
485     }
486 
487     /**
488         Draws the puppet
489     */
490     final void draw() {
491         this.selfSort();
492 
493         foreach(rootPart; rootParts) {
494             if (!rootPart.renderEnabled) continue;
495             rootPart.drawOne();
496         }
497     }
498 
499     /**
500         Removes a parameter from this puppet
501     */
502     final void removeParameter(Parameter param) {
503         import std.algorithm.searching : countUntil;
504         import std.algorithm.mutation : remove;
505         ptrdiff_t idx = parameters.countUntil(param);
506         if (idx >= 0) {
507             parameters = parameters.remove(idx);
508         }
509     }
510 
511     /**
512         Gets this puppet's root transform
513     */
514     final Transform transform() {
515         return puppetRootNode.transform;
516     }
517 
518     /**
519         Rescans the puppet's nodes
520 
521         Run this every time you change the layout of the puppet's node tree
522     */
523     final void rescanNodes() {
524         this.scanParts!false(root);
525     }
526 
527     /**
528         Updates the texture state for all texture slots.
529     */
530     final void updateTextureState() {
531 
532         // Update filtering mode for texture slots
533         foreach(texutre; textureSlots) {
534             texutre.setFiltering(meta.preservePixels ? Filtering.Point : Filtering.Linear);
535         }
536     }
537 
538     /**
539         Finds Node by its name
540     */
541     T find(T = Node)(string name) if (is(T : Node)) {
542         return cast(T)findNode(root, name);
543     }
544 
545     /**
546         Finds Node by its unique id
547     */
548     T find(T = Node)(uint uuid) if (is(T : Node)) {
549         return cast(T)findNode(root, uuid);
550     }
551 
552     /**
553         Returns all the parts in the puppet
554     */
555     Part[] getAllParts() {
556         return findNodesType!Part(root);
557     }
558 
559     /**
560         Finds nodes based on their type
561     */
562     final T[] findNodesType(T)(Node n) if (is(T : Node)) {
563         T[] nodes;
564 
565         if (T item = cast(T)n) {
566             nodes ~= item;
567         }
568 
569         // Recurse through children
570         foreach(child; n.children) {
571             nodes ~= findNodesType!T(child);
572         }
573 
574         return nodes;
575     }
576 
577     /**
578         Adds a texture to a new slot if it doesn't already exist within this puppet
579     */
580     final uint addTextureToSlot(Texture texture) {
581         import std.algorithm.searching : canFind;
582 
583         // Add texture if we can't find it.
584         if (!textureSlots.canFind(texture)) textureSlots ~= texture;
585         return cast(uint)textureSlots.length-1;
586     }
587 
588     /**
589         Populate texture slots with all visible textures in the model
590     */
591     final void populateTextureSlots() {
592         if (textureSlots.length > 0) textureSlots.length = 0;
593         
594         foreach(part; getAllParts) {
595             foreach(texture; part.textures) {
596                 this.addTextureToSlot(texture);
597             }
598         }
599     }
600 
601     /**
602         Sets thumbnail of this puppet
603     */
604     final void setThumbnail(Texture texture) {
605         if (this.meta.thumbnailId == NO_THUMBNAIL) {
606             this.meta.thumbnailId = this.addTextureToSlot(texture);
607         } else {
608             textureSlots[this.meta.thumbnailId] = texture;
609         }
610     }
611 
612     /**
613         Gets the texture slot index for a texture
614 
615         returns -1 if none was found
616     */
617     final ptrdiff_t getTextureSlotIndexFor(Texture texture) {
618         import std.algorithm.searching : countUntil;
619         return textureSlots.countUntil(texture);
620     }
621 
622     /**
623         Clears this puppet's thumbnail
624 
625         By default it does not delete the texture assigned, pass in true to delete texture
626     */
627     final void clearThumbnail(bool deleteTexture = false) {
628         import std.algorithm.mutation : remove;
629         if (deleteTexture) textureSlots = remove(textureSlots, this.meta.thumbnailId);
630         this.meta.thumbnailId = NO_THUMBNAIL;
631     }
632 
633     /**
634         This cursed toString implementation outputs the puppet's
635         nodetree as a pretty printed tree.
636 
637         Please use a graphical viewer instead of this if you can,
638         eg. Inochi Creator.
639     */
640     override
641     string toString() {
642         import std.format : format;
643         import std.range : repeat, takeExactly;
644         import std.array : array;
645         bool[] lineSet;
646 
647         string toStringBranch(Node n, int indent, bool showLines = true) {
648 
649             lineSet ~= n.children.length > 0;
650             string getLineSet() {
651                 if (indent == 0) return "";
652                 string s = "";
653                 foreach(i; 1..lineSet.length) {
654                     s ~= lineSet[i-1] ? "│ " : "  ";
655                 }
656                 return s;
657             }
658 
659             string iden = getLineSet();
660 
661             string s = "%s[%s] %s <%s>\n".format(n.children.length > 0 ? "╭─" : "", n.typeId, n.name, n.uuid);
662             foreach(i, child; n.children) {
663                 string term = "├→";
664                 if (i == n.children.length-1) {
665                     term = "╰→";
666                     lineSet[indent] = false;
667                 }
668                 s ~= "%s%s%s".format(iden, term, toStringBranch(child, indent+1));
669             }
670 
671             lineSet.length--;
672 
673             return s;
674         }
675 
676         return toStringBranch(root, 0);
677     }
678 
679     /**
680         Serializes a puppet
681     */
682     void serialize(S)(ref S serializer) {
683         auto state = serializer.objectBegin;
684             serializer.putKey("meta");
685             serializer.serializeValue(meta);
686             serializer.putKey("physics");
687             serializer.serializeValue(physics);
688             serializer.putKey("nodes");
689             serializer.serializeValue(root);
690             serializer.putKey("param");
691             serializer.serializeValue(parameters);
692             serializer.putKey("automation");
693             serializer.serializeValue(automation);
694         serializer.objectEnd(state);
695     }
696 
697     /**
698         Deserializes a puppet
699     */
700     SerdeException deserializeFromFghj(Fghj data) {
701         if (auto exc = data["meta"].deserializeValue(this.meta)) return exc;
702         if (!data["physics"].isEmpty)
703             if (auto exc = data["physics"].deserializeValue(this.physics)) return exc;
704         if (auto exc = data["nodes"].deserializeValue(this.root)) return exc;
705         if (auto exc = data["param"].deserializeValue(this.parameters)) return exc;
706 
707         // Deserialize automation
708         foreach(key; data["automation"].byElement) {
709             string type;
710             if (auto exc = key["type"].deserializeValue(type)) return exc;
711 
712             if (inHasAutomationType(type)) {
713                 auto auto_ = inInstantiateAutomation(type, this);
714                 auto_.deserializeFromFghj(key);
715                 this.automation ~= auto_;
716             }
717         }
718         this.finalizeDeserialization(data);
719 
720         return null;
721     }
722 
723     /**
724         Finalizer
725     */
726     void finalizeDeserialization(Fghj data) {
727         this.root.setPuppet(this);
728         this.root.name = "Root";
729         this.puppetRootNode = new Node(this);
730         this.root.finalize();
731         foreach(parameter; parameters) {
732             parameter.finalize(this);
733         }
734         foreach(automation_; automation) {
735             automation_.finalize(this);
736         }
737         this.scanParts!true(this.root);
738         this.selfSort();
739     }
740 
741     /**
742         Gets the internal root parts array 
743 
744         Do note that some root parts may be Composites instead.
745     */
746     final Node[] getRootParts() {
747         return rootParts;
748     }
749 }