1 /++
2     Add-on module for loading 2D puppets and displaying them in the game.
3     <$(HTTP https://github.com/Inochi2D/inochi2d/wiki, INP Format information)
4 
5     Currently supported:
6     - Loading .inp format including JSON data and textures.
7     - Displays nodes of type Part (the rest are ignored)
8     - Reading the transformation of the node, identifier, type, Mesh data, transparency.
9     - Setting the 'part' texture.
10     - Saving to an .inp file.
11     - Sorting zoffset.
12 
13     Not implemented:
14     - Mask mod.
15     - Path Deform.
16     - JointBindingData.
17 
18     There are some modifications that are not described in the reference:
19     - It is not necessary to specify the index data.
20 
21     For rendering, you need a context. The format is loaded in the following way:
22     ---
23     Puppet puppet = new Puppet();
24     puppet.load("data.inp"); // or puppet.loadFromMem(...);
25     ---
26 
27     The function `render.draw` is used for displaying, since this is an IDrawable object:
28     ---
29     render.draw(puppet, Vecf(32, 32));
30     ---
31 
32     Authors implementations for Tida: $(HTTP https://github.com/TodNaz, TodNaz)
33     Authors INP format: $(HTTP https://github.com/LunaTheFoxgirl, LunaTheFoxgirl)
34     License: $(HTTP https://opensource.org/licenses/MIT, MIT)
35 +/
36 module tida.puppet;
37 
38 import std.json;
39 
40 import tida.gl;
41 import tida.drawable;
42 import tida.vertgen;
43 import tida.matrix;
44 import tida.render;
45 import tida.image;
46 import tida.texture;
47 import tida.shader;
48 import tida.vector;
49 import tida.angle;
50 import tida.shape;
51 import tida.color;
52 
53 private bool isKeyExists(JSONValue json, string key) @trusted {
54     try {
55         auto item = json[key];
56         return true;
57     }catch(Exception e) {
58         return false;
59     }
60 }
61 
62 alias UUID = int;
63 
64 enum MaskMode : string{
65     Mask = "Mask", DodgeMask = "DodgeMask"
66 }
67 
68 /// Node type
69 enum PuppetNodeType : string {
70     Node = "Node",
71     Drawable = "Drawable",
72     PathDeform = "PathDeform",
73     Part = "Part",
74     Mask = "Mask"
75 }
76 
77 /// Transformation structure for the matrix.
78 struct Transform
79 {
80     public
81     {
82         float[3] trans; /// Translate vector
83         bool[3] transLock; /// Translate lock
84         float[3] rot; /// Rotate vector
85         bool[3] rotLock; /// Rotate lock
86         Vecf scaling; /// Scale vector
87         bool[2] scaleLock; // Scale lock
88     }
89 
90     void parseJSON(JSONValue json) @trusted
91     {
92         trans[0] = json["trans"][0].get!float;
93         trans[1] = json["trans"][1].get!float;
94         trans[2] = json["trans"][2].get!float;
95 
96         if(json.isKeyExists("trans_lock"))
97         {
98             transLock[0] = json["trans_lock"][0].get!bool;
99             transLock[1] = json["trans_lock"][1].get!bool;
100             transLock[2] = json["trans_lock"][2].get!bool;
101         }
102 
103         rot[0] = json["rot"][0].get!float;
104         rot[1] = json["rot"][1].get!float;
105         rot[2] = json["rot"][2].get!float;
106 
107         if(json.isKeyExists("rot_lock"))
108         {
109             rotLock[0] = json["rot_lock"][0].get!bool;
110             rotLock[1] = json["rot_lock"][1].get!bool;
111             rotLock[2] = json["rot_lock"][2].get!bool;
112         }
113 
114         scaling.x = json["scale"][0].get!float;
115         scaling.y = json["scale"][1].get!float;
116 
117         if(json.isKeyExists("scale_lock"))
118         {
119             scaleLock[0] = json["scale_lock"][0].get!bool;
120             scaleLock[1] = json["scale_lock"][1].get!bool;
121         }
122     }
123 
124     JSONValue toJSON() @trusted
125     {
126         JSONValue json;
127 
128         json["trans"] = JSONValue(trans);
129         if(json.isKeyExists("trans_lock")) json["trans_lock"] = JSONValue(transLock);
130         json["rot"] = JSONValue(rot);
131         if(json.isKeyExists("rot_lock")) json["rot_lock"] = JSONValue(rotLock);
132         json["scale"] = JSONValue(scaling.array);
133         if(json.isKeyExists("scale_lock")) json["scale_lock"] = JSONValue(scaleLock);
134 
135         return json;
136     }
137 
138     /// Returns the model of the matrix by the parameters of the structure.
139     float[4][4] modelMatrix() @safe @property nothrow pure
140     {
141         float[4][4] mat = identity();
142 
143         mat = translate(mat,    this.transLock[0] ? 0.0 : trans[0],
144                                 this.transLock[1] ? 0.0 : trans[1],
145                                 this.transLock[2] ? 0.0 : trans[2]);
146         mat = eulerRotate(mat,  this.rotLock[0] ? 0.0 : -rot[0],
147                                 this.rotLock[1] ? 0.0 : -rot[1],
148                                 this.rotLock[2] ? 0.0 : -rot[2]);
149         mat = scale(mat,    this.scaleLock[0] ? 1.0 : scaling.x,
150                             this.scaleLock[1] ? 1.0 : scaling.y);
151 
152         return mat;
153     }
154 }
155 
156 /// Information about the puppet.
157 struct PuppetMeta
158 {
159     public
160     {
161         string name; /// 
162         string ver = "v1.0.0-alpha"; ///
163         string[] authors; ///
164         string copyright; ///
165         string contact; /// Mail / address / website.
166         string reference = "https://github.com/Inochi2D/inochi2d/"; ///
167         uint thumbnailID; /// Puppet thumbnail.
168     }
169 
170     void parseJSON(JSONValue json) @trusted
171     {
172         name = json["name"].str;
173         ver = json["version"].str;
174         if(json.isKeyExists("thumbnail_id")) thumbnailID = cast(uint) json["thumbnail_id"].integer;
175     }
176 
177     JSONValue toJSON() @trusted
178     {
179         JSONValue json;
180 
181         json["name"] = JSONValue(name);
182         json["version"] = JSONValue(ver);
183         json["thumbnail_id"] = JSONValue(thumbnailID);
184 
185         return json;
186     }
187 }
188 
189 class PuppetNode : IDrawable
190 {
191     import std.random;
192 
193     public
194     {
195         PuppetNodeType type; ///
196         UUID uuid; /// identificator
197         string name = "Unnamed"; ///
198         bool enabled = true; /// Do I need to display the node.
199         float zsort = 0.0; /// zsort value
200         Transform transform; ///
201         PuppetNode[] children; ////
202 
203         float[4][4] transformMatrix;
204         PuppetNode parent;
205     }
206 
207     protected
208     {
209         Puppet puppet;
210     }
211 
212     float zSort() @safe @property {
213         return parent !is null ? parent.zSort + zsort : zsort;
214     }
215 
216     this(PuppetNode parent = null) @safe {
217         type = PuppetNodeType.Node;
218         uuid = uniform!UUID;
219         this.parent = parent;
220     }
221 
222     this(Puppet puppet, PuppetNode parent = null) @safe {
223         this.puppet = puppet;
224         type = PuppetNodeType.Node;
225         uuid = uniform!UUID;
226         this.parent = parent;
227     }
228 
229     void parseJSON(JSONValue json, Puppet puppet) @trusted
230     {
231         this.puppet = puppet;
232 
233         uuid = cast(UUID) json["uuid"].integer;
234         if(json.isKeyExists("name")) name = json["name"].str;
235         enabled = json["enabled"].get!bool;
236         zsort = json["zsort"].get!float;
237         transform.parseJSON(json["transform"]);
238 
239         if(parent is null)
240             transformMatrix = transform.modelMatrix();
241         else
242             transformMatrix = mulmat(transform.modelMatrix(), parent.transformMatrix);
243 
244         if(json.isKeyExists("children"))
245         foreach(e; json["children"].array) {
246             PuppetNode node = jsonToNode(e, puppet, this);
247             children ~= node;
248         }
249     }
250 
251     JSONValue toJSON() @trusted
252     {
253         JSONValue json;
254 
255         json["type"] = JSONValue(cast(string) type);
256         json["uuid"] = JSONValue(uuid);
257         if(name != []) json["name"] = JSONValue(name);
258         json["enabled"] = JSONValue(enabled);
259         json["transform"] = transform.toJSON();
260         json["zsort"] = JSONValue(zsort);
261 
262         json["children"] = [null];
263 
264         foreach(e; children) {
265             json["children"].array ~= e.toJSON();
266         }
267 
268         json["children"].array = json["children"].array[1 .. $];
269 
270         return json;
271     }
272 
273     override void draw(IRenderer render, Vecf position) @safe
274     {
275         if(!enabled) return;
276     }
277 }
278 
279 PuppetNode jsonToNode(JSONValue json, Puppet puppet, PuppetNode parent = null) @trusted
280 {
281     PuppetNodeType type = cast(PuppetNodeType) json["type"].str;
282 
283     switch(type)
284     {
285         case PuppetNodeType.Node:
286             PuppetNode node = new PuppetNode(parent);
287             node.type = type;
288             node.parseJSON(json, puppet);
289 
290             return node;
291 
292         case PuppetNodeType.Drawable:
293             goto default;
294 
295         case PuppetNodeType.PathDeform:
296             PuppetPathDeform node = new PuppetPathDeform(parent);
297             node.type = type;
298             node.parseJSON(json, puppet);
299 
300             return node;
301 
302         case PuppetNodeType.Part:
303             PuppetPart node = new PuppetPart(parent);
304             node.type = type;
305             node.parseJSON(json, puppet);
306 
307             return node;
308 
309         case PuppetNodeType.Mask:
310             PuppetMask node = new PuppetMask(parent);
311             node.type = type;
312             node.parseJSON(json, puppet);
313 
314             return node;
315 
316         default:
317             return null;
318     }
319 }
320 
321 /// Geometry information.
322 struct MeshData
323 {
324     public
325     {
326         float[] vertsOld; 
327         float[] verts; /// 
328         float[] uvs; ///
329         uint[] indices; // is not a ushort
330     }
331 
332     private
333     {
334         bool textureCoordinatePut = false;
335     }
336 
337     void parseJSON(JSONValue json) @trusted
338     {
339         foreach(e; json["verts"].array) 
340             verts ~= e.get!float;
341 
342         if(json.isKeyExists("uvs"))
343         foreach(e; json["uvs"].array)
344             uvs ~= e.get!float;
345 
346         if(json.isKeyExists("indices"))
347         foreach(e; json["indices"].array)
348             indices ~= cast(uint) e.integer;
349 
350         this.textured();
351     }
352 
353     void textured() @safe {
354         vertsOld = verts.dup;
355 
356         Vecf[] vertxs;
357         for(int i = 0; i < verts.length; i += 2) {
358             vertxs ~= Vecf(verts[i], verts[i + 1]);
359         }
360 
361         auto vertDump = vertxs;
362         vertxs.length = 0;
363 
364         if(uvs.length == 0)
365         {
366             const clip = rectVertexs(vertDump);
367 
368             foreach(e; vertDump) {
369                 vertxs ~= [e,
370                             Vecf((e.x - clip.x) / clip.width,
371                                  (e.y - clip.y) / clip.height)];
372             }
373 
374             verts = vertxs.generateArray;
375         }else
376         {
377             Vecf[] uvvs;
378             for(int i = 0; i < uvs.length; i += 2) {
379                 uvvs ~= Vecf(uvs[i], uvs[i + 1]);
380             }
381 
382             foreach(i; 0 .. vertDump.length) {
383                 vertxs ~= [vertDump[i], uvvs[i]];
384             }
385 
386             verts = vertxs.generateArray;
387         }
388 
389         textureCoordinatePut = true;
390     }
391 
392     JSONValue toJSON() @trusted
393     {
394         JSONValue json;
395 
396         json["verts"] = textureCoordinatePut ? JSONValue(vertsOld) : JSONValue(verts);
397         json["uvs"] = JSONValue(uvs);
398         json["indices"] = JSONValue(indices);
399 
400         return json;
401     }
402 }
403 
404 abstract class PuppetDrawable : PuppetNode
405 {
406     public
407     {
408         MeshData mesh; ///
409 
410         VertexInfo!float vertInfo; ///
411     }
412 
413     override void parseJSON(JSONValue json, Puppet puppet) @trusted
414     {
415         super.parseJSON(json, puppet);
416 
417         mesh.parseJSON(json["mesh"]);
418         generateVertexInfo();
419     }
420 
421     override JSONValue toJSON() @trusted
422     {
423         auto json = super.toJSON();
424 
425         json["mesh"] = mesh.toJSON();
426 
427         return json;
428     }
429 
430     void generateVertexInfo() @safe
431     {
432         vertInfo = new VertexInfo!float();
433         
434         if(mesh.indices != []) {
435             vertInfo.bindFromBufferAndElem(mesh.verts, mesh.indices);
436         } else {
437             vertInfo.bindFromBuffer(mesh.verts);
438         }
439     }
440 }
441 
442 /// Texture display node
443 class PuppetPart : PuppetDrawable
444 {
445     import std.random;
446 
447     public
448     {
449         uint[] textures; ///
450         float opacity; ///
451         MaskMode maskMode; ///
452         float maskThreshold; ///
453         UUID[] maskedBy; ///
454     }
455 
456     this(PuppetNode parent = null) @safe {
457         type = PuppetNodeType.Part;
458         uuid = uniform!uint;
459         this.parent = parent;
460     }
461 
462     this(Puppet puppet, PuppetNode parent = null) @safe 
463     {
464         type = PuppetNodeType.Part;
465         this.puppet = puppet;
466         uuid = uniform!uint;
467         this.parent = parent;
468     }
469 
470     override void parseJSON(JSONValue json, Puppet puppet) @trusted
471     {
472         super.parseJSON(json, puppet);
473 
474         foreach(e; json["textures"].array)
475             textures ~= cast(uint) e.integer;
476 
477         opacity = json["opacity"].get!float;
478         maskMode = cast(MaskMode) json["mask_mode"].str;
479         maskThreshold = json["mask_threshold"].get!float;
480 
481         if(json.isKeyExists("masked_by"))
482         foreach(e; json["masked_by"].array)
483             maskedBy ~= cast(UUID) e.integer;
484     }
485 
486     override JSONValue toJSON() @trusted
487     {
488         auto json = super.toJSON();
489 
490         json["textures"] = JSONValue(textures);
491         json["opacity"] = JSONValue(opacity);
492         json["mask_mode"] = JSONValue(maskMode);
493         json["mask_threshold"] = JSONValue(maskThreshold);
494         json["masked_by"] = JSONValue(maskedBy);
495 
496         return json;
497     }
498 
499     void drawTexture(IRenderer render, Texture texture, Vecf position) @trusted 
500     {
501         Shader!Program shader = texture.initShader(render);
502         VertexInfo!float vinfo = vertInfo;
503 
504         vinfo.bindVertexArray();
505         glBindBuffer(GL_ARRAY_BUFFER, vinfo.idBufferArray);
506        	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vinfo.idElementArray);
507 
508         glEnableVertexAttribArray(shader.getAttribLocation("position"));
509        	glVertexAttribPointer(shader.getAttribLocation("position"), 2, GL_FLOAT, false, 4 * float.sizeof, null);
510 
511         glEnableVertexAttribArray(shader.getAttribLocation("aTexCoord"));
512         glVertexAttribPointer(shader.getAttribLocation("aTexCoord"), 2, GL_FLOAT, false, 4 * float.sizeof,
513             cast(void*) (2 * float.sizeof));
514 
515         float[4][4] proj = (cast(GLRender) render).projection();
516         float[4][4] model = transformMatrix;
517 
518         model = translate(model, position.x, position.y, 0.0f);
519 
520         shader.using();
521 
522         glActiveTexture(GL_TEXTURE0);
523         texture.bind();
524 
525         if(shader.getUniformLocation("projection") != -1)
526         shader.setUniform("projection", proj);
527 
528         if(shader.getUniformLocation("model") != -1)
529         shader.setUniform("model", model);
530 
531         if(shader.getUniformLocation("color") != -1)
532         shader.setUniform("color", rgba(255, 255, 255, cast(ubyte) (ubyte.max * opacity)));
533 
534         if(vinfo.elementLength != 0)
535             glDrawElements(GL_TRIANGLES, cast(uint) vinfo.elementLength, GL_UNSIGNED_INT, null);
536         else
537             glDrawArrays(GL_TRIANGLES, 0, cast(uint) vinfo.length / 4);
538 
539         glBindBuffer(GL_ARRAY_BUFFER, 0);
540         glBindVertexArray(0);
541        	glBindTexture(GL_TEXTURE_2D, 0);
542 
543         render.resetShader();
544     }
545 
546     override void draw(IRenderer render, Vecf position) @safe {
547         foreach(e; textures) {
548             drawTexture(render, puppet.dataTexture[e].texture, position);
549         }
550     }
551 }
552 
553 ///
554 class PuppetMask : PuppetDrawable 
555 {
556     this(PuppetNode parent = null) @safe
557     {
558         this.parent = parent;
559     }
560 
561     this(Puppet puppet, PuppetNode parent) @safe
562     {
563         this.parent = parent;
564         this.puppet = puppet;
565     }
566 }
567 
568 ///
569 struct PuppetJointBindingData
570 {
571     public
572     {
573         UUID boundTo;
574         size_t[][] bindData;
575     }
576 }
577 
578 ///
579 class PuppetPathDeform : PuppetNode
580 {
581     public
582     {
583         Vecf[] joints;
584         PuppetJointBindingData[] bindings;
585     }
586 
587     this(PuppetNode parent = null) @safe
588     {
589         this.parent = parent;
590     }
591 
592     this(Puppet puppet, PuppetNode parent) @safe
593     {
594         this.parent = parent;
595         this.puppet = puppet;
596     }
597 }
598 
599 private T byteTo(T)(ubyte[] bytes) @trusted
600 {
601     T data = T.init;
602     data =  bytes[0] << 24 |
603             bytes[1] << 16 |
604             bytes[2] << 8  |
605             bytes[3];
606 
607     return data;
608 }
609 
610 
611 private ubyte[4] toByte(T)(T value) @trusted
612 {
613     ubyte[4] ab;
614     for (int i = 0; i < 4; i++)
615         ab[3 - i] = cast(ubyte) (value >> (i * 8));
616 
617     return ab;
618 }
619 
620 ///
621 class Puppet : IDrawable
622 {
623     import std.file : read;
624     import imagefmt;
625 
626     public
627     {
628         PuppetMeta meta; /// Puppet information
629         PuppetNode node; ///
630         Image[] dataTexture; ///
631     }
632 
633     protected
634     {
635         PuppetNode[] nodesDraw;
636     }
637 
638     /++
639         Loads a puppet from a mem.
640 
641         Params:
642             data = Raw data.
643 
644         Throws: 'Exception' if the file header is not found / damaged, 
645                 as well as if there is no image data. If you only need to 
646                 load JSON data, then just use the 'parseJSON' function.
647     +/
648     void loadFromMem(ubyte[] data) @trusted 
649     {
650         import std.stdio;
651 
652         struct TextureBlob {
653             ubyte encode;
654             ubyte[] data;
655         }
656 
657         TextureBlob[] blobs;
658 
659         if(data[0 .. 8] != ['T','R','N','S','R','T','S','\0'])
660             throw new Exception("It's a not a INP file!");
661 
662         immutable len = byteTo!int(data[8 .. 8 + 4]);
663         string jsonString = cast(string) data[8 + 4 .. 8 + 4 + len];
664 
665         if(data[len + 8 + 4 .. len + 8 + 4 + 8] != ['T','E','X','_','S','E','C','T'])
666             throw new Exception("Not find \"TEX_SECT\"!");
667 
668         int offset = len + 16 + 4;
669         immutable texcount = byteTo!int(data[offset .. offset += 4]);
670 
671         foreach(_; 0 .. texcount) {
672             immutable texLeng = byteTo!int(data[offset .. offset += 4]);
673             immutable texType = data[offset++];
674             blobs ~= TextureBlob(texType, data[offset .. offset += texLeng]);
675         }
676 
677         Image[] images;
678         foreach(e; blobs) {
679             Image image = new Image();
680 
681             IFImage temp = read_image(e.data, 4);
682             scope(exit) temp.free();
683             assert(temp.e == 0, IF_ERROR[temp.e]);
684 
685             image.allocatePlace(temp.w,temp.h);
686             image.bytes!(PixelFormat.RGBA)(temp.buf8);
687 
688             image.toTexture();
689 
690             dataTexture ~= image;
691         }
692 
693         this.parseJSON(jsonString.parseJSON);
694     }
695 
696     /++
697         Loads a puppet from a file.
698 
699         Params:
700             file = path to the file.
701 
702         Throws: 'Exception' if the file header is not found / damaged, 
703                 as well as if there is no image data. If you only need to 
704                 load JSON data, then just use the 'parseJSON' function.
705     +/
706     void load(string file) @trusted {
707         loadFromMem(cast(ubyte[]) read(file));
708     }
709 
710     ///
711     ubyte[] toRaw() @trusted
712     {
713         ubyte[] data = cast(ubyte[]) "TRNSRTS\0";
714 
715         string jsonString = this.toJSON().toString();
716         data ~= toByte!int(cast(int) jsonString.length);
717         data ~= cast(ubyte[]) jsonString;
718 
719         data ~= cast(ubyte[]) "TEX_SECT";
720         data ~= toByte!int(cast(int) dataTexture.length);
721 
722         foreach(e; dataTexture) {
723             int err;
724             ubyte[] textData = write_image_mem(4, e.width, e.height, e.bytes!(PixelFormat.RGBA), 2, err);
725             data ~= toByte!int(cast(int) textData.length);
726             data ~= 0;
727             data ~= textData;
728         }
729 
730         return data;
731     }
732 
733     void scan(PuppetNode node) @safe {
734         if(node is null) return;
735         if(!node.enabled) return;
736 
737         if(node.type == PuppetNodeType.Part) this.nodesDraw ~= node;
738         foreach(e; node.children)
739             scan(e);
740     }
741 
742     void sortNodeDraw() @safe {
743         import std.algorithm : sort;
744 
745         sort!((a, b) => a.zSort > b.zSort)(nodesDraw);
746     }
747 
748     void update() @safe
749     {
750         scan(node);
751         sortNodeDraw();
752     }
753 
754     ///
755     void parseJSON(JSONValue json) @trusted {
756         meta.parseJSON(json["meta"]);
757         node = jsonToNode(json["nodes"], this);
758 
759         update();
760     }
761 
762     string nodesDrawsToString() @trusted {
763         string str = "[";
764         foreach(PuppetNode e; nodesDraw) {
765             str ~= e.toJSON().toString() ~ ",";
766         }
767         return str[0 .. $ - 1] ~ "]";
768     }
769 
770     ///
771     JSONValue toJSON() @trusted {
772         JSONValue json;
773         json["meta"] = meta.toJSON();
774         json["nodes"] = node.toJSON();
775 
776         return json;
777     }
778 
779     override void draw(IRenderer render, Vecf position) @safe
780     {
781         foreach(e; nodesDraw) e.draw(render, position);
782     }
783 }
784 
785 /// Find a node by name.
786 PuppetNode nodeByName(PuppetNode node, string name) @trusted {
787     if(node.name == name) return node;
788 
789     foreach(e; node.children) if(nodeByName(e, name) !is null) return e;
790 
791     return null;
792 }
793 
794 /// Find a node by identificator.
795 PuppetNode nodeByUUID(PuppetNode node, UUID uuid) @trusted {
796     if(node.uuid == uuid) return node;
797 
798     foreach(e; node.children) if(nodeByUUID(e, uuid) !is null) return e;
799 
800     return null;
801 }