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: 9 апр. 2017 г.
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 <core/alloc.h>
23 #include <core/status.h>
24 #include <core/3d/Scene3D.h>
25 #include <core/3d/Object3D.h>
26 
27 namespace lsp
28 {
Object3D(Scene3D * scene,const LSPString * name)29     Object3D::Object3D(Scene3D *scene, const LSPString *name)
30     {
31         pScene                  = scene;
32         bVisible                = true;
33 
34         sName.set(name);
35         dsp::init_matrix3d_identity(&sMatrix);
36 
37         dsp::init_point_xyz(&sBoundBox.p[0], 0.0f, 0.0f, 0.0f);
38         dsp::init_point_xyz(&sBoundBox.p[1], 0.0f, 0.0f, 0.0f);
39         dsp::init_point_xyz(&sBoundBox.p[2], 0.0f, 0.0f, 0.0f);
40         dsp::init_point_xyz(&sBoundBox.p[3], 0.0f, 0.0f, 0.0f);
41         dsp::init_point_xyz(&sBoundBox.p[4], 0.0f, 0.0f, 0.0f);
42         dsp::init_point_xyz(&sBoundBox.p[5], 0.0f, 0.0f, 0.0f);
43         dsp::init_point_xyz(&sBoundBox.p[6], 0.0f, 0.0f, 0.0f);
44         dsp::init_point_xyz(&sBoundBox.p[7], 0.0f, 0.0f, 0.0f);
45 
46         dsp::init_point_xyz(&sCenter, 0.0f, 0.0f, 0.0f);
47     }
48 
~Object3D()49     Object3D::~Object3D()
50     {
51         destroy();
52     }
53 
destroy()54     void Object3D::destroy()
55     {
56         vTriangles.flush();
57     }
58 
post_load()59     void Object3D::post_load()
60     {
61         dsp::init_point_xyz(&sCenter, 0.0f, 0.0f, 0.0f);
62         for (size_t i=0; i<8; ++i)
63         {
64             sCenter.x      += sBoundBox.p[i].x;
65             sCenter.y      += sBoundBox.p[i].y;
66             sCenter.z      += sBoundBox.p[i].z;
67         }
68         sCenter.x      *= 0.125f; // 1/8
69         sCenter.y      *= 0.125f; // 1/8
70         sCenter.z      *= 0.125f; // 1/8
71     }
72 
add_triangle(ssize_t face_id,ssize_t v1,ssize_t v2,ssize_t v3,ssize_t vn1,ssize_t vn2,ssize_t vn3)73     status_t Object3D::add_triangle(
74             ssize_t face_id,
75             ssize_t v1, ssize_t v2, ssize_t v3,
76             ssize_t vn1, ssize_t vn2, ssize_t vn3
77         )
78     {
79         // Check vertex index
80         ssize_t v_limit  = pScene->vVertexes.size();
81         if ((v1 >= v_limit) || (v2 >= v_limit) || (v3 >= v_limit))
82             return -STATUS_INVALID_VALUE;
83         if ((v1 < 0) || (v2 < 0) || (v3 < 0))
84             return -STATUS_INVALID_VALUE;
85 
86         // Check normal index
87         ssize_t n_limit  = pScene->vNormals.size();
88         if ((vn1 >= n_limit) || (vn2 >= n_limit) || (vn3 >= n_limit))
89             return -STATUS_INVALID_VALUE;
90 
91         // Allocate triangle
92         ssize_t tid         = pScene->vTriangles.size();
93         obj_triangle_t *t   = pScene->vTriangles.alloc();
94         if (t == NULL)
95             return -STATUS_NO_MEM;
96 
97         // Store vertexes
98         t->id       = tid;
99         t->face     = face_id;
100         t->ptag     = NULL;
101         t->itag     = -1;
102         t->v[0]     = pScene->vertex(v1);
103         t->v[1]     = pScene->vertex(v2);
104         t->v[2]     = pScene->vertex(v3);
105 
106         // Store normals
107         obj_normal_t *xvn       = NULL;
108         if ((vn1 < 0) || (vn2 < 0) || (vn3 < 0))
109         {
110             // Add normal
111             xvn         = pScene->vXNormals.alloc();
112             if (xvn == NULL)
113                 return -STATUS_NO_MEM;
114 
115             // Get points
116             dsp::calc_normal3d_p3(xvn, t->v[0], t->v[1], t->v[2]);
117         }
118 
119         t->n[0]     = (vn1 >= 0) ? pScene->normal(vn1) : xvn;
120         t->n[1]     = (vn2 >= 0) ? pScene->normal(vn2) : xvn;
121         t->n[2]     = (vn3 >= 0) ? pScene->normal(vn3) : xvn;
122 
123         // Store edges
124         for (size_t i=0; i<3; ++i)
125         {
126             // Lookup for already existing edge
127             obj_edge_t *e = register_edge(t->v[i], t->v[(i+1)%3]);
128             if (e == NULL)
129                 return STATUS_NO_MEM;
130 
131             // Add edge to triangle
132             t->e[i]     = e;
133         }
134 
135         bool first = vTriangles.size() <= 0;
136         if (!vTriangles.add(t))
137             return STATUS_NO_MEM;
138 
139         // Commit triangle edges
140         if (first)
141         {
142             for (size_t i=0; i<8; ++i)
143                 sBoundBox.p[i] = *(t->v[0]);
144         }
145         else
146             calc_bound_box(t->v[0]);
147 
148         calc_bound_box(t->v[1]);
149         calc_bound_box(t->v[2]);
150 
151         return STATUS_OK;
152     }
153 
calc_bound_box()154     void Object3D::calc_bound_box()
155     {
156         obj_triangle_t **vt = vTriangles.get_array();
157         for (size_t i=0, n=vTriangles.size(); i<n; ++i)
158         {
159             obj_triangle_t *t = *(vt++);
160             if (i == 0)
161             {
162                 for (size_t i=0; i<8; ++i)
163                     sBoundBox.p[i] = *(t->v[0]);
164             }
165             else
166                 calc_bound_box(t->v[0]);
167 
168             calc_bound_box(t->v[1]);
169             calc_bound_box(t->v[2]);
170         }
171     }
172 
register_edge(obj_vertex_t * v0,obj_vertex_t * v1)173     obj_edge_t *Object3D::register_edge(obj_vertex_t *v0, obj_vertex_t *v1)
174     {
175         // Lookup for already existing edge
176         obj_edge_t *e = v0->ve;
177         while (e != NULL)
178         {
179             if (e->v[0] == v0)
180             {
181                 if (e->v[1] == v1)
182                     break;
183                 e = e->vlnk[0];
184             }
185             else // e->v[1] == v0
186             {
187                 if (e->v[0] == v1)
188                     break;
189                 e = e->vlnk[1];
190             }
191         }
192 
193         // Need to create new edge and link?
194         if (e == NULL)
195         {
196             ssize_t res = pScene->vEdges.ialloc(&e);
197             if (res < 0)
198                 return NULL;
199 
200             e->id       = res;
201             e->v[0]     = v0;
202             e->v[1]     = v1;
203             e->vlnk[0]  = v0->ve;
204             e->vlnk[1]  = v1->ve;
205             e->ptag     = NULL;
206             e->itag     = -1;
207 
208             v0->ve      = e;
209             v1->ve      = e;
210         }
211 
212         return e;
213     }
214 
calc_bound_box(const obj_vertex_t * p)215     void Object3D::calc_bound_box(const obj_vertex_t *p)
216     {
217         obj_boundbox_t *b = &sBoundBox;
218 
219         // Left plane
220         if (b->p[0].x > p->x)
221             b->p[0].x = p->x;
222         if (b->p[1].x > p->x)
223             b->p[1].x = p->x;
224         if (b->p[4].x > p->x)
225             b->p[4].x = p->x;
226         if (b->p[5].x > p->x)
227             b->p[5].x = p->x;
228 
229         // Right plane
230         if (b->p[2].x < p->x)
231             b->p[2].x = p->x;
232         if (b->p[3].x < p->x)
233             b->p[3].x = p->x;
234         if (b->p[6].x < p->x)
235             b->p[6].x = p->x;
236         if (b->p[7].x < p->x)
237             b->p[7].x = p->x;
238 
239         // Near plane
240         if (b->p[1].y > p->y)
241             b->p[1].y = p->y;
242         if (b->p[2].y > p->y)
243             b->p[2].y = p->y;
244         if (b->p[5].y > p->y)
245             b->p[5].y = p->y;
246         if (b->p[6].y > p->y)
247             b->p[6].y = p->y;
248 
249         // Far plane
250         if (b->p[0].y < p->y)
251             b->p[0].y = p->y;
252         if (b->p[3].y < p->y)
253             b->p[3].y = p->y;
254         if (b->p[4].y < p->y)
255             b->p[4].y = p->y;
256         if (b->p[7].y < p->y)
257             b->p[7].y = p->y;
258 
259         // Top plane
260         if (b->p[0].z < p->z)
261             b->p[0].z = p->z;
262         if (b->p[1].z < p->z)
263             b->p[1].z = p->z;
264         if (b->p[2].z < p->z)
265             b->p[2].z = p->z;
266         if (b->p[3].z < p->z)
267             b->p[3].z = p->z;
268 
269         // Bottom plane
270         if (b->p[4].z > p->z)
271             b->p[4].z = p->z;
272         if (b->p[5].z > p->z)
273             b->p[5].z = p->z;
274         if (b->p[6].z > p->z)
275             b->p[6].z = p->z;
276         if (b->p[7].z > p->z)
277             b->p[7].z = p->z;
278     }
279 
add_triangle(ssize_t * vv,ssize_t * vn)280     status_t Object3D::add_triangle(ssize_t *vv, ssize_t *vn)
281     {
282         return add_triangle(vv[0], vv[1], vv[2], vn[0], vn[1], vn[2]);
283     }
284 
add_triangle(ssize_t * vv)285     status_t Object3D::add_triangle(ssize_t *vv)
286     {
287         return add_triangle(vv[0], vv[1], vv[2], -1, -1, -1);
288     }
289 
290 } /* namespace lsp */
291