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