1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 14 мая 2019 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <ui/tk/tk.h>
23 
24 namespace lsp
25 {
26     namespace tk
27     {
28         const w_class_t LSPMesh3D::metadata     = { "LSPMesh3D", &LSPObject3D::metadata };
29 
LSPMesh3D(LSPDisplay * dpy)30         LSPMesh3D::LSPMesh3D(LSPDisplay *dpy):
31             LSPObject3D(dpy),
32             sColor(this)
33         {
34             pClass          = &metadata;
35 
36             dsp::init_matrix3d_identity(&sMatrix);
37             dsp::init_point_xyz(&sPov, 0.0f, 0.0f, 0.0f);
38         }
39 
~LSPMesh3D()40         LSPMesh3D::~LSPMesh3D()
41         {
42             do_destroy();
43         }
44 
do_destroy()45         void LSPMesh3D::do_destroy()
46         {
47             for (size_t i=0, n=vLayers.size(); i<n; ++i)
48             {
49                 mesh_layer_t *layer = vLayers.get(i);
50                 if (layer == NULL)
51                     continue;
52 
53                 if (layer->pdata != NULL)
54                 {
55                     free_aligned(layer->pdata);
56                     layer->mesh     = NULL;
57                     layer->normals  = NULL;
58                     layer->vbuffer  = NULL;
59                     layer->nbuffer  = NULL;
60                 }
61             }
62 
63             vLayers.clear();
64         }
65 
init()66         status_t LSPMesh3D::init()
67         {
68             status_t res = LSPObject3D::init();
69             if (res != STATUS_OK)
70                 return res;
71 
72             init_color(C_RED, &sColor);
73             init_color(C_YELLOW, &sColor);
74 
75             ssize_t id = sSlots.add(LSPSLOT_DRAW3D, slot_draw3d, self());
76             return (id >= 0) ? STATUS_OK : -id;
77         }
78 
destroy()79         void LSPMesh3D::destroy()
80         {
81             do_destroy();
82             LSPObject3D::destroy();
83         }
84 
get_position(point3d_t * dst)85         void LSPMesh3D::get_position(point3d_t *dst)
86         {
87             dsp::init_point_xyz(dst, 0.0f, 0.0f, 0.0f);
88             dsp::apply_matrix3d_mp1(dst, &sMatrix);
89         }
90 
clear()91         void LSPMesh3D::clear()
92         {
93             do_destroy();
94             query_draw();
95         }
96 
slot_draw3d(LSPWidget * sender,void * ptr,void * data)97         status_t LSPMesh3D::slot_draw3d(LSPWidget *sender, void *ptr, void *data)
98         {
99             if ((ptr == NULL) || (data == NULL))
100                 return STATUS_BAD_ARGUMENTS;
101 
102             LSPArea3D *_this   = widget_ptrcast<LSPArea3D>(ptr);
103             return (_this != NULL) ? _this->on_draw3d(static_cast<IR3DBackend *>(data)) : STATUS_BAD_ARGUMENTS;
104         }
105 
on_draw3d(IR3DBackend * r3d)106         status_t LSPMesh3D::on_draw3d(IR3DBackend *r3d)
107         {
108             return STATUS_OK;
109         }
110 
add_triangles(const point3d_t * mesh,const point3d_t * normals,size_t items)111         status_t LSPMesh3D::add_triangles(const point3d_t *mesh, const point3d_t *normals, size_t items)
112         {
113             if ((mesh == NULL) || (items % 3))
114                 return STATUS_INVALID_VALUE;
115 
116             // Allocate new layer and initialize
117             mesh_layer_t layer;
118 
119             layer.type          = LT_TRIANGLES;
120             layer.mesh          = NULL;
121             layer.normals       = NULL;
122             layer.vbuffer       = NULL;
123             layer.nbuffer       = NULL;
124             layer.primitives    = items / 3;
125             layer.draw          = 0;
126             layer.pdata         = NULL;
127             layer.rebuild       = true;
128 
129             // Estimate size of buffers
130             size_t vbytes       = sizeof(point3d_t) * items;
131             size_t nbytes       = sizeof(vector3d_t) * items;
132 
133             // Allocate buffers for the layer
134             uint8_t *ptr        = alloc_aligned<uint8_t>(layer.pdata, (vbytes+nbytes)*2, DEFAULT_ALIGN);
135             if (ptr == NULL)
136                 return STATUS_NO_MEM;
137 
138             layer.mesh          = reinterpret_cast<point3d_t *>(ptr);
139             ptr                += vbytes;
140             layer.vbuffer       = reinterpret_cast<point3d_t *>(ptr);
141             ptr                += vbytes;
142             layer.normals       = reinterpret_cast<vector3d_t *>(ptr);
143             ptr                += nbytes;
144             layer.nbuffer       = reinterpret_cast<vector3d_t *>(ptr);
145             ptr                += nbytes;
146 
147             // Copy data to the buffer
148             ::memcpy(layer.mesh, mesh, sizeof(point3d_t) * items);
149 
150             // Copy normal data if present or generate if not
151             if (normals != NULL)
152                 ::memcpy(layer.normals, mesh, sizeof(vector3d_t) * items);
153             else
154             {
155                 const point3d_t *p  = mesh;
156                 vector3d_t *n       = layer.normals;
157                 for (size_t i=0; i<layer.primitives; ++i, n += 3, p += 3)
158                 {
159                     dsp::calc_normal3d_pv(&n[0], p);
160                     n[1]    = n[0];
161                     n[2]    = n[0];
162                 }
163             }
164 
165             // Try to add layer
166             if (!vLayers.add(layer))
167             {
168                 free_aligned(layer.pdata);
169                 return STATUS_NO_MEM;
170             }
171 
172             query_draw();
173             return STATUS_OK;
174         }
175 
add_lines(const point3d_t * mesh,size_t items)176         status_t LSPMesh3D::add_lines(const point3d_t *mesh, size_t items)
177         {
178             if ((mesh == NULL) || (items & 1))
179                 return STATUS_INVALID_VALUE;
180 
181             // Allocate new layer and initialize
182             mesh_layer_t layer;
183 
184             layer.type          = LT_LINES;
185             layer.mesh          = NULL;
186             layer.normals       = NULL;
187             layer.vbuffer       = NULL;
188             layer.nbuffer       = NULL;
189             layer.primitives    = items >> 1;
190             layer.draw          = 0;
191             layer.pdata         = NULL;
192             layer.rebuild       = true;
193 
194             // Estimate size of buffers
195             size_t vbytes       = sizeof(point3d_t) * items;
196 
197             // Allocate buffers for the layer
198             uint8_t *ptr        = alloc_aligned<uint8_t>(layer.pdata, vbytes*2, DEFAULT_ALIGN);
199             if (ptr == NULL)
200                 return STATUS_NO_MEM;
201 
202             layer.mesh          = reinterpret_cast<point3d_t *>(ptr);
203             ptr                += vbytes;
204             layer.vbuffer       = reinterpret_cast<point3d_t *>(ptr);
205             ptr                += vbytes;
206 
207             // Copy data to the buffer
208             ::memcpy(layer.mesh, mesh, sizeof(point3d_t) * items);
209 
210             // Try to add layer
211             if (!vLayers.add(layer))
212             {
213                 free_aligned(layer.pdata);
214                 return STATUS_NO_MEM;
215             }
216 
217             query_draw();
218             return STATUS_OK;
219         }
220 
mark_for_rebuild()221         void LSPMesh3D::mark_for_rebuild()
222         {
223             for (size_t i=0, n=vLayers.size(); i<n; ++i)
224             {
225                 mesh_layer_t *layer = vLayers.get(i);
226                 if (layer != NULL)
227                     layer->rebuild      = true;
228             }
229             query_draw();
230         }
231 
set_transform(const matrix3d_t * matrix)232         void LSPMesh3D::set_transform(const matrix3d_t *matrix)
233         {
234             sMatrix     = *matrix;
235             mark_for_rebuild();
236         }
237 
set_view_point(const point3d_t * pov)238         void LSPMesh3D::set_view_point(const point3d_t *pov)
239         {
240             LSPObject3D::set_view_point(pov);
241             sPov        = *pov;
242             mark_for_rebuild();
243         }
244 
render(IR3DBackend * r3d)245         void LSPMesh3D::render(IR3DBackend *r3d)
246         {
247             // Visible?
248             if (!is_visible())
249                 return;
250 
251             // Maybe need a sync?
252             sSlots.execute(LSPSLOT_DRAW3D, this, r3d);
253 
254             // Perform draw
255             r3d_buffer_t buf;
256             buf.width           = 1.0f;
257 
258             buf.vertex.stride   = sizeof(point3d_t);
259             buf.normal.stride   = sizeof(vector3d_t);
260             buf.color.data      = NULL;
261             buf.color.stride    = 0;
262             buf.index.data      = NULL;
263 
264             // Need to rebuild scene?
265             for (size_t i=0, n=vLayers.size(); i<n; ++i)
266             {
267                 mesh_layer_t *layer = vLayers.get(i);
268                 if (layer == NULL)
269                     continue;
270 
271                 buf.vertex.data     = layer->vbuffer;
272 
273                 switch (layer->type)
274                 {
275                     case LT_TRIANGLES:
276                         rebuild_triangles(layer);
277 
278                         buf.count           = layer->draw;
279                         buf.type            = R3D_PRIMITIVE_TRIANGLES;
280                         buf.flags           = R3D_BUFFER_LIGHTING;
281                         buf.normal.data     = layer->nbuffer;
282 
283                         buf.color.dfl.r     = sColor.red();
284                         buf.color.dfl.g     = sColor.green();
285                         buf.color.dfl.b     = sColor.blue();
286                         buf.color.dfl.a     = 1.0f;
287                         break;
288 
289                     case LT_LINES:
290                         rebuild_lines(layer);
291 
292                         buf.count           = layer->draw;
293                         buf.type            = R3D_PRIMITIVE_LINES;
294                         buf.flags           = 0;
295                         buf.normal.data     = NULL;
296 
297                         buf.color.dfl.r     = sLineColor.red();
298                         buf.color.dfl.g     = sLineColor.green();
299                         buf.color.dfl.b     = sLineColor.blue();
300                         buf.color.dfl.a     = 1.0f;
301                         break;
302 
303                     default:
304                         continue;
305                 }
306 
307                 r3d->draw_primitives(&buf);
308             }
309         }
310 
rebuild_triangles(mesh_layer_t * layer)311         void LSPMesh3D::rebuild_triangles(mesh_layer_t *layer)
312         {
313             if (!layer->rebuild)
314                 return;
315             layer->rebuild          = false;
316 
317             const point3d_t *sp     = layer->mesh;
318             const vector3d_t *sn    = layer->normals;
319 
320             point3d_t *dp           = layer->vbuffer;
321             vector3d_t *dn          = layer->nbuffer;
322 
323             point3d_t p[3];
324             vector3d_t n[3], pl;
325 
326             // Perform rebuild relative to the point of view
327             layer->draw     = 0;
328             for (size_t i=0; i<layer->primitives; ++i, sp += 3, sn += 3)
329             {
330                 // Apply transformation to points
331                 dsp::apply_matrix3d_mp2(&p[0], &sp[0], &sMatrix);
332                 dsp::apply_matrix3d_mp2(&p[1], &sp[1], &sMatrix);
333                 dsp::apply_matrix3d_mp2(&p[2], &sp[2], &sMatrix);
334 
335                 // Apply transformation to normals
336                 dsp::apply_matrix3d_mv2(&n[0], &sn[0], &sMatrix);
337                 dsp::apply_matrix3d_mv2(&n[1], &sn[1], &sMatrix);
338                 dsp::apply_matrix3d_mv2(&n[2], &sn[2], &sMatrix);
339 
340                 // Compute plane equation and location of POV to the plane
341                 dsp::calc_plane_pv(&pl, p);
342                 float d         = sPov.x*pl.dx + sPov.y*pl.dy + sPov.z*pl.dz + pl.dw;
343 
344                 // Emit the result to the view
345                 if (d >= 0.0f)
346                 {
347                     // Use regular order of vertices
348                     dp[0]       = p[0];
349                     dp[1]       = p[1];
350                     dp[2]       = p[2];
351 
352                     // Use regular order of normals
353                     dn[0]       = n[0];
354                     dn[1]       = n[1];
355                     dn[2]       = n[2];
356                 }
357                 else if (d <= -DSP_3D_TOLERANCE)
358                 {
359                     // Reverse order of vertices
360                     dp[0]       = p[0];
361                     dp[1]       = p[2];
362                     dp[2]       = p[1];
363 
364                     // Reverse order of normals and flip normals
365                     dsp::flip_vector_v2(&dn[0], &n[0]);
366                     dsp::flip_vector_v2(&dn[1], &n[2]);
367                     dsp::flip_vector_v2(&dn[2], &n[1]);
368                 }
369                 else
370                     continue;
371 
372                 // Update pointers and number of primitives
373                 dn += 3;
374                 dp += 3;
375                 ++ layer->draw;
376             }
377         }
378 
rebuild_lines(mesh_layer_t * layer)379         void LSPMesh3D::rebuild_lines(mesh_layer_t *layer)
380         {
381             if (!layer->rebuild)
382                 return;
383             layer->rebuild          = false;
384 
385             const point3d_t *sp     = layer->mesh;
386             point3d_t *dp           = layer->vbuffer;
387 
388             for (size_t i=0, n = layer->primitives << 1; i<n; ++i, ++dp, ++sp)
389                 dsp::apply_matrix3d_mp2(dp, sp, &sMatrix);
390 
391             layer->draw             = layer->primitives;
392         }
393 
394     } /* namespace tk */
395 } /* namespace lsp */
396