1 module inochi2d.godot.nodes.puppet;
2 import inochi2d.godot.resources.puppet;
3 import inochi2d.godot.render;
4 import inochi2d.core.render;
5 import inochi2d.core.mesh;
6 import inochi2d.puppet;
7 import godot.rendering_server;
8 import godot.rendering_device;
9 import godot.viewport;
10 import godot.node2d;
11 import godot.image;
12 import godot.world2d;
13 import godot;
14 import numem;
15 
16 /**
17     An Inochi2D Puppet in the godot scene tree.
18 */
19 @class_name("Inochi2DPuppet")
20 class Puppet2D : Node2D {
21 private:
22 @nogc:
23     Viewport viewport_;
24     Puppet2DState state_;
25     PuppetResource resource_;
26     Puppet puppet_;
27 
28 protected:
29 
30     /**
31         Updates and renders the puppet to its internal draw list.
32     */
33     override void process_(double delta) {
34         state_.update(delta);
35     }
36 
37 public:
38 
39     /// Destructor
40     ~this() {
41         if (resource_)
42             gd_delete(resource_);
43 
44         if (state_)
45             nogc_delete(state_);
46     }
47 
48     /**
49         Name of the loaded puppet.
50     */
51     @gd_export_custom(PROPERTY_HINT_ONESHOT, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY)
52     final @property String name() => String(puppet_ ? puppet_.properties.name : null);
53 
54     /**
55         Author of the loaded puppet.
56     */
57     @gd_export_custom(PROPERTY_HINT_ONESHOT, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY)
58     final @property String author() => String(puppet_ ? puppet_.properties.author : null);
59 
60     /**
61         The puppet resource
62     */
63     @gd_export final @property PuppetResource puppet() => resource_;
64     @gd_export final @property void puppet(PuppetResource value) {
65         if (state_)
66             nogc_delete(state_);
67 
68         if (puppet_)
69             nogc_delete(puppet_);
70 
71         this.resource_ = gde_refswap(resource_, value);
72         if (resource_) {
73             this.puppet_ = resource_.realize();
74             this.state_ = nogc_new!Puppet2DState(this);
75         }
76     }
77 
78     /**
79         The actual loaded puppet instance.
80     */
81     @gd_hide final @property Puppet instance() => puppet_;
82 }
83 mixin GodotClass!Puppet2D;
84 
85 /**
86     The internal rendering state of the puppet.
87 
88     This wraps all the memory managment needed from godot.
89 */
90 class Puppet2DState : NuObject {
91 private:
92 @nogc:
93     //
94     //              VARIABLES
95     //
96     Puppet2D puppet;
97     RenderingDevice device;
98 
99 
100 
101 
102     //
103     //              TEXTURE MANAGMENT
104     //
105     RDTexture[] textures;
106 
107     void loadTextures() {
108         import godot.core.gdextension;
109         import core.stdc.stdio : printf;
110         printf("%p %p\n", device, device.ptr);
111 
112         TextureCache cache = puppet.instance.textureCache;
113         this.textures = nu_malloca!RDTexture(cache.cache.length);
114         foreach(i, Texture texture; cache.cache) {
115 
116             // Only load formats that Godot understands.
117             if (texture.format.toGodotFormat() != TextureDataFormat.DATA_FORMAT_MAX) {
118 
119                 // Create texture format info.
120                 RDTextureFormat p_format = gd_new!RDTextureFormat();
121                 p_format.textureType = TextureType.TEXTURE_TYPE_2D;
122                 p_format.format = texture.format.toGodotFormat();
123                 p_format.samples = TextureSamples.TEXTURE_SAMPLES_1;
124                 p_format.usageBits = TextureUsageFlags.TEXTURE_USAGE_SAMPLING_BIT;
125                 p_format.width = texture.width;
126                 p_format.height = texture.height;
127                 p_format.depth = 1;
128                 p_format.arrayLayers = 1;
129                 p_format.mipmaps = 1;
130                 p_format.addShareableFormat(texture.format.toGodotFormat());
131 
132                 // Create texture view with default settings.
133                 RDTextureView p_view = gd_new!RDTextureView();
134                 gde_ref(p_view);
135 
136                 // Create texture.
137                 this.textures[i] = nogc_new!RDTexture(device, p_format, p_view, cast(ubyte[])texture.data.data);
138             }
139         }
140     }
141     
142     void freeTextures() {
143         if (textures.length > 0) {
144             nu_freea(textures);
145         }
146     }
147 
148 
149 
150 
151     //
152     //              SHADERS
153     //
154     RDShader baseShader;
155 
156 
157 
158     //
159     //              MESH MANAGMENT
160     //
161     RDVertexBuffer vertexBuffer;
162     RDIndexBuffer indexBuffer;
163 
164     void updateVertexData() {
165         auto vertices = puppet.instance.drawList.vertices;
166         auto indices = puppet.instance.drawList.indices;
167 
168         // Create or resize vertex buffer if needed.
169         if (!vertexBuffer || vertexBuffer.size < vertices.length*VtxData.sizeof) {
170             if (vertexBuffer)
171                 nogc_delete(vertexBuffer);
172 
173             vertexBuffer = nogc_new!RDVertexBuffer(device, vertices.length*VtxData.sizeof, cast(BufferCreationFlags)0);
174         }
175         vertexBuffer.update(cast(void[])vertices, 0);
176 
177         // Create or resize index buffer if needed.
178         if (!indexBuffer || indexBuffer.size < indices.length*uint.sizeof) {
179             if (indexBuffer)
180                 nogc_delete(indexBuffer);
181 
182             indexBuffer = nogc_new!RDIndexBuffer(device, IndexBufferFormat.INDEX_BUFFER_FORMAT_UINT32, cast(uint)indices.length, cast(BufferCreationFlags)0);
183         }
184         indexBuffer.update(cast(void[])indices, 0);
185     }
186 
187 
188 
189 
190     //
191     //              RENDERING
192     //
193     void drawToGodot() {
194 
195         // Update all vertex data mappings before rendering.
196         this.updateVertexData();
197 
198         // Go through the commands
199         foreach(ref DrawCmd command; puppet.instance.drawList.commands) {
200 
201         }
202     }
203 
204 
205 public:
206 
207     /**
208         The viewport being rendered to.
209     */
210     final @property Viewport viewport() => puppet.getViewport();
211 
212     // Destructor
213     ~this() {
214         this.freeTextures();
215     }
216 
217     /**
218         Constructs a puppet rendering state object.
219 
220         Params:
221             puppet = The puppet to wrap state for.
222     */
223     this(Puppet2D puppet) {
224         this.puppet = puppet;
225         this.device = RenderingServer.instance.getRenderingDevice();
226         this.loadTextures();
227     }
228 
229     /**
230         Updates and renders the puppet.
231 
232         Params:
233             delta = Time since last frame.
234     */
235     void update(float delta) {
236         puppet.instance.update(delta);
237         puppet.instance.draw(delta);
238         this.drawToGodot();
239     }
240 }
241 
242 /**
243     A managed godot framebuffer, basically a collection of a 
244     Viewport and Canvas. 
245 */
246 struct GodotFramebuffer {
247 public:
248 @nogc:
249     RID viewport;
250     RID canvas;
251     RID canvasItem;
252 
253     /**
254         Creates a new framebuffer.
255     */
256     static GodotFramebuffer create() {
257         GodotFramebuffer fbo;
258         fbo.viewport = RenderingServer.instance.viewportCreate();
259         fbo.canvas = RenderingServer.instance.canvasCreate();
260         fbo.canvasItem = RenderingServer.instance.canvasItemCreate();
261         RenderingServer.instance.viewportAttachCanvas(fbo.viewport, fbo.canvas);
262         RenderingServer.instance.viewportSetDisable3d(fbo.viewport, true);
263         return fbo;
264     }
265 
266     /**
267         Frees this framebuffer.
268     */
269     void free() {
270         RenderingServer.instance.freeRid(viewport);
271         RenderingServer.instance.freeRid(canvas);
272     
273         viewport = RID.init;
274         canvas = RID.init;
275     }
276 }
277 
278 TextureDataFormat toGodotFormat(TextureFormat format) @nogc nothrow pure {
279     final switch(format) with(TextureFormat) {
280         case none:
281             return TextureDataFormat.DATA_FORMAT_MAX;
282         
283         case rgba8Unorm:
284             return TextureDataFormat.DATA_FORMAT_R8G8B8A8_UNORM;
285         
286         case r8:
287             return TextureDataFormat.DATA_FORMAT_R8_UNORM;
288 
289         case depthStencil:
290             return TextureDataFormat.DATA_FORMAT_D24_UNORM_S8_UINT;
291     }
292 }