Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2012
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / Scene Compositor sub-project
9 : *
10 : * GPAC is free software; you can redistribute it and/or modify
11 : * it under the terms of the GNU Lesser General Public License as published by
12 : * the Free Software Foundation; either version 2, or (at your option)
13 : * any later version.
14 : *
15 : * GPAC is distributed in the hope that it will be useful,
16 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : * GNU Lesser General Public License for more details.
19 : *
20 : * You should have received a copy of the GNU Lesser General Public
21 : * License along with this library; see the file COPYING. If not, write to
22 : * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 : *
24 : */
25 :
26 :
27 : #include "visual_manager.h"
28 : #include "nodes_stacks.h"
29 :
30 :
31 : #ifndef GPAC_DISABLE_VRML
32 :
33 : #ifndef GPAC_DISABLE_3D
34 :
35 : #include <gpac/options.h>
36 : #include <gpac/mediaobject.h>
37 :
38 19454 : void drawable_3d_base_traverse(GF_Node *n, void *rs, Bool is_destroy, void (*build_shape)(GF_Node*,Drawable3D *,GF_TraverseState *) )
39 : {
40 : GF_TraverseState *tr_state = (GF_TraverseState *)rs;
41 19454 : Drawable3D *stack = (Drawable3D*)gf_node_get_private(n);
42 :
43 19454 : if (is_destroy) {
44 184 : drawable_3d_del(n);
45 184 : return;
46 : }
47 19270 : if (gf_node_dirty_get(n)) {
48 320 : mesh_reset(stack->mesh);
49 320 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Rebuilding mesh %s\n", gf_node_get_class_name(n)));
50 320 : build_shape(n, stack, tr_state);
51 320 : gf_node_dirty_clear(n, 0);
52 : }
53 19270 : switch (tr_state->traversing_mode) {
54 15614 : case TRAVERSE_DRAW_3D:
55 15614 : visual_3d_draw(tr_state, stack->mesh);
56 15614 : drawable3d_check_focus_highlight(n, tr_state, &stack->mesh->bounds);
57 15614 : break;
58 2592 : case TRAVERSE_GET_BOUNDS:
59 2592 : tr_state->bbox = stack->mesh->bounds;
60 2592 : break;
61 910 : case TRAVERSE_PICK:
62 910 : visual_3d_vrml_drawable_pick(n, tr_state, stack->mesh, NULL);
63 910 : return;
64 154 : case TRAVERSE_SORT:
65 : //we are drawing 3D object but configured for 2D, force 3D
66 154 : if (!tr_state->visual->type_3d && tr_state->visual->compositor->hybrid_opengl) {
67 54 : tr_state->visual->compositor->root_visual_setup=0;
68 54 : tr_state->visual->compositor->force_type_3d=1;
69 : }
70 : }
71 : }
72 :
73 54 : static void build_shape_box(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state)
74 : {
75 54 : mesh_new_box(stack->mesh, ((M_Box *)n)->size);
76 54 : }
77 :
78 11691 : static void TraverseBox(GF_Node *n, void *rs, Bool is_destroy)
79 : {
80 11691 : drawable_3d_base_traverse(n, rs, is_destroy, build_shape_box);
81 11691 : }
82 :
83 55 : void compositor_init_box(GF_Compositor *compositor, GF_Node *node)
84 : {
85 55 : drawable_3d_new(node);
86 55 : gf_node_set_callback_function(node, TraverseBox);
87 55 : }
88 :
89 12 : static void build_shape_cone(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state)
90 : {
91 : M_Cone *co = (M_Cone *)n;
92 12 : mesh_new_cone(stack->mesh, co->height, co->bottomRadius, co->bottom, co->side, tr_state->visual->compositor->fast);
93 12 : }
94 :
95 626 : static void TraverseCone(GF_Node *n, void *rs, Bool is_destroy)
96 : {
97 626 : drawable_3d_base_traverse(n, rs, is_destroy, build_shape_cone);
98 626 : }
99 :
100 12 : void compositor_init_cone(GF_Compositor *compositor, GF_Node *node)
101 : {
102 12 : drawable_3d_new(node);
103 12 : gf_node_set_callback_function(node, TraverseCone);
104 12 : }
105 :
106 21 : static void build_shape_cylinder(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state)
107 : {
108 : M_Cylinder *cy = (M_Cylinder *)n;
109 21 : mesh_new_cylinder(stack->mesh, cy->height, cy->radius, cy->bottom, cy->side, cy->top, tr_state->visual->compositor->fast);
110 21 : }
111 :
112 1717 : static void TraverseCylinder(GF_Node *n, void *rs, Bool is_destroy)
113 : {
114 1717 : drawable_3d_base_traverse(n, rs, is_destroy, build_shape_cylinder);
115 1717 : }
116 :
117 21 : void compositor_init_cylinder(GF_Compositor *compositor, GF_Node *node)
118 : {
119 21 : drawable_3d_new(node);
120 21 : gf_node_set_callback_function(node, TraverseCylinder);
121 21 : }
122 :
123 13 : static void build_shape_sphere(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state)
124 : {
125 : M_Sphere *sp = (M_Sphere *)n;
126 13 : mesh_new_sphere(stack->mesh, sp->radius, tr_state->visual->compositor->fast, NULL);
127 13 : }
128 :
129 100 : static void get_tx_coords_from_angle(GF_TraverseState *tr_state, GF_TextureHandler *txh, Bool horizontal, u32 *min_coord, u32 *max_coord)
130 : {
131 : GF_Vec target, ref;
132 : Fixed dot, det, hfov, theta_angle, angle_start, angle_end, min_tx, max_tx;
133 : u32 dim;
134 :
135 100 : ref.x = horizontal ? FIX_ONE : 0;
136 100 : ref.y = horizontal ? 0 : FIX_ONE;
137 : ref.z = 0;
138 :
139 100 : target = camera_get_target_dir(tr_state->camera);
140 100 : if (horizontal) target.y = 0;
141 50 : else target.x = 0;
142 :
143 100 : gf_vec_norm(&target);
144 100 : dot = gf_vec_dot(target, ref);
145 100 : if (horizontal) {
146 50 : det = target.x*ref.z - target.z*ref.x;
147 : } else {
148 50 : det = target.y*ref.z - target.z*ref.y;
149 : }
150 100 : theta_angle = gf_atan2(det, dot);
151 : //sphere starts horizontally at -PI/2
152 100 : if (horizontal) {
153 50 : theta_angle -= GF_PI2;
154 50 : hfov = tr_state->camera->fieldOfView*tr_state->camera->width/tr_state->camera->height/2;
155 : } else {
156 50 : hfov = tr_state->camera->fieldOfView/2;
157 : }
158 :
159 100 : angle_start = theta_angle - hfov;
160 100 : angle_end = theta_angle + hfov;
161 :
162 : //move everything in [-PI,PI]
163 100 : if (angle_start < -GF_PI) angle_start += GF_2PI;
164 100 : if (angle_start > GF_PI) angle_start -= GF_2PI;
165 100 : if (angle_end < -GF_PI) angle_end += GF_2PI;
166 100 : if (angle_end > GF_PI) angle_end -= GF_2PI;
167 :
168 100 : if (horizontal) {
169 : //start angle corresponds to max tx horiz coord, left to min
170 50 : max_tx = FIX_ONE - (angle_start + GF_PI) / GF_2PI;
171 50 : min_tx = FIX_ONE - (angle_end + GF_PI) / GF_2PI;
172 : //we wrap horizontally, which means we may have min_tx > max_tx, in which case we need both [min_tx, WIDTH] and [0, max_tx] parts of the texture
173 50 : dim = txh->width;
174 : } else {
175 : //-angle is the same position as +angle
176 50 : if (angle_start<0) angle_start = -angle_start;
177 50 : if (angle_end<0) angle_end = -angle_end;
178 : //SRD x=0 is the top of the sphere - we don't wrap vertically
179 50 : if (angle_start<angle_end) {
180 50 : min_tx = angle_start / GF_PI;
181 50 : max_tx = angle_end / GF_PI;
182 : } else {
183 0 : max_tx = angle_start / GF_PI;
184 0 : min_tx = angle_end / GF_PI;
185 : }
186 50 : dim = txh->height;
187 : }
188 100 : *min_coord= (u32) (min_tx*dim);
189 100 : *max_coord= (u32) (max_tx*dim);
190 100 : }
191 :
192 1590 : static void TraverseSphere(GF_Node *n, void *rs, Bool is_destroy)
193 : {
194 : GF_TraverseState *tr_state = (GF_TraverseState *)rs;
195 1590 : drawable_3d_base_traverse(n, rs, is_destroy, build_shape_sphere);
196 :
197 1590 : if (!is_destroy && tr_state->traversing_mode == TRAVERSE_DRAW_3D) {
198 : GF_MediaObjectVRInfo vrinfo;
199 : u32 min_x, max_x, min_y, max_y;
200 919 : GF_TextureHandler *txh = gf_sc_texture_get_handler( ((M_Appearance *) tr_state->appear)->texture );
201 :
202 1788 : if (!txh || !txh->stream) return;
203 :
204 202 : if (!gf_mo_get_srd_info(txh->stream, &vrinfo) || !vrinfo.is_tiled_srd)
205 : return;
206 :
207 : //we need to compute min/max tex coords visible for that sphere
208 :
209 50 : get_tx_coords_from_angle(tr_state, txh, GF_TRUE, &min_x, &max_x);
210 50 : get_tx_coords_from_angle(tr_state, txh, GF_FALSE, &min_y, &max_y);
211 :
212 50 : gf_mo_hint_visible_rect(txh->stream, min_x, max_x, min_y, max_y);
213 50 : GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Visible texture rectangle of sphere is %u,%u,%u,%u\n", min_x, max_x, min_y, max_y));
214 : }
215 : }
216 :
217 19 : void compositor_init_sphere(GF_Compositor *compositor, GF_Node *node)
218 : {
219 19 : drawable_3d_new(node);
220 19 : gf_node_set_callback_function(node, TraverseSphere);
221 19 : }
222 :
223 7 : static void build_shape_point_set(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state)
224 : {
225 : M_PointSet *ps = (M_PointSet *)n;
226 7 : mesh_new_ps(stack->mesh, ps->coord, ps->color);
227 7 : }
228 153 : static void TraversePointSet(GF_Node *n, void *rs, Bool is_destroy)
229 : {
230 153 : drawable_3d_base_traverse(n, rs, is_destroy, build_shape_point_set);
231 153 : }
232 :
233 7 : void compositor_init_point_set(GF_Compositor *compositor, GF_Node *node)
234 : {
235 7 : drawable_3d_new(node);
236 7 : gf_node_set_callback_function(node, TraversePointSet);
237 7 : }
238 :
239 27 : static void build_shape_ifs(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state)
240 : {
241 27 : mesh_new_ifs(stack->mesh, n);
242 27 : }
243 2474 : static void TraverseIFS(GF_Node *n, void *rs, Bool is_destroy)
244 : {
245 2474 : drawable_3d_base_traverse(n, rs, is_destroy, build_shape_ifs);
246 2474 : }
247 :
248 0 : static void IFS_SetColorIndex(GF_Node *node, GF_Route *route)
249 : {
250 : M_IndexedFaceSet *ifs = (M_IndexedFaceSet *)node;
251 0 : if (node) {
252 0 : gf_sg_vrml_field_copy(&ifs->colorIndex, &ifs->set_colorIndex, GF_SG_VRML_MFINT32);
253 0 : gf_sg_vrml_mf_reset(&ifs->set_colorIndex, GF_SG_VRML_MFINT32);
254 : }
255 0 : }
256 :
257 0 : static void IFS_SetCoordIndex(GF_Node *node, GF_Route *route)
258 : {
259 : M_IndexedFaceSet *ifs = (M_IndexedFaceSet *)node;
260 0 : if (node) {
261 0 : gf_sg_vrml_field_copy(&ifs->coordIndex, &ifs->set_coordIndex, GF_SG_VRML_MFINT32);
262 0 : gf_sg_vrml_mf_reset(&ifs->set_coordIndex, GF_SG_VRML_MFINT32);
263 : }
264 0 : }
265 :
266 0 : static void IFS_SetNormalIndex(GF_Node *node, GF_Route *route)
267 : {
268 : M_IndexedFaceSet *ifs = (M_IndexedFaceSet *)node;
269 0 : if (node) {
270 0 : gf_sg_vrml_field_copy(&ifs->normalIndex, &ifs->set_normalIndex, GF_SG_VRML_MFINT32);
271 0 : gf_sg_vrml_mf_reset(&ifs->set_normalIndex, GF_SG_VRML_MFINT32);
272 : }
273 0 : }
274 :
275 0 : static void IFS_SetTexCoordIndex(GF_Node *node, GF_Route *route)
276 : {
277 : M_IndexedFaceSet *ifs = (M_IndexedFaceSet *)node;
278 0 : if (node) {
279 0 : gf_sg_vrml_field_copy(&ifs->texCoordIndex, &ifs->set_texCoordIndex, GF_SG_VRML_MFINT32);
280 0 : gf_sg_vrml_mf_reset(&ifs->set_texCoordIndex, GF_SG_VRML_MFINT32);
281 : }
282 0 : }
283 :
284 33 : void compositor_init_ifs(GF_Compositor *compositor, GF_Node *node)
285 : {
286 : M_IndexedFaceSet *ifs = (M_IndexedFaceSet *)node;
287 33 : drawable_3d_new(node);
288 33 : gf_node_set_callback_function(node, TraverseIFS);
289 33 : ifs->on_set_colorIndex = IFS_SetColorIndex;
290 33 : ifs->on_set_coordIndex = IFS_SetCoordIndex;
291 33 : ifs->on_set_normalIndex = IFS_SetNormalIndex;
292 33 : ifs->on_set_texCoordIndex = IFS_SetTexCoordIndex;
293 :
294 : #ifdef GPAC_ENABLE_COVERAGE
295 33 : if (gf_sys_is_cov_mode()) {
296 : IFS_SetCoordIndex(NULL, NULL);
297 : IFS_SetColorIndex(NULL, NULL);
298 : IFS_SetNormalIndex(NULL, NULL);
299 : IFS_SetTexCoordIndex(NULL, NULL);
300 : }
301 : #endif
302 :
303 33 : }
304 :
305 8 : static void build_shape_ils(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state)
306 : {
307 : M_IndexedLineSet *ils = (M_IndexedLineSet *)n;
308 8 : mesh_new_ils(stack->mesh, ils->coord, &ils->coordIndex, ils->color, &ils->colorIndex, ils->colorPerVertex, 0);
309 8 : }
310 :
311 156 : static void TraverseILS(GF_Node *n, void *rs, Bool is_destroy)
312 : {
313 156 : drawable_3d_base_traverse(n, rs, is_destroy, build_shape_ils);
314 156 : }
315 :
316 0 : static void ILS_SetColorIndex(GF_Node *node, GF_Route *route)
317 : {
318 : M_IndexedLineSet *ils = (M_IndexedLineSet *)node;
319 0 : if (node) {
320 0 : gf_sg_vrml_field_copy(&ils->colorIndex, &ils->set_colorIndex, GF_SG_VRML_MFINT32);
321 0 : gf_sg_vrml_mf_reset(&ils->set_colorIndex, GF_SG_VRML_MFINT32);
322 : }
323 0 : }
324 :
325 0 : static void ILS_SetCoordIndex(GF_Node *node, GF_Route *route)
326 : {
327 : M_IndexedLineSet *ils = (M_IndexedLineSet *)node;
328 0 : if (node) {
329 0 : gf_sg_vrml_field_copy(&ils->coordIndex, &ils->set_coordIndex, GF_SG_VRML_MFINT32);
330 0 : gf_sg_vrml_mf_reset(&ils->set_coordIndex, GF_SG_VRML_MFINT32);
331 : }
332 0 : }
333 :
334 8 : void compositor_init_ils(GF_Compositor *compositor, GF_Node *node)
335 : {
336 : M_IndexedLineSet *ils = (M_IndexedLineSet *)node;
337 8 : drawable_3d_new(node);
338 8 : gf_node_set_callback_function(node, TraverseILS);
339 8 : ils->on_set_colorIndex = ILS_SetColorIndex;
340 8 : ils->on_set_coordIndex = ILS_SetCoordIndex;
341 :
342 : #ifdef GPAC_ENABLE_COVERAGE
343 8 : if (gf_sys_is_cov_mode()) {
344 : ILS_SetCoordIndex(NULL, NULL);
345 : ILS_SetColorIndex(NULL, NULL);
346 : }
347 : #endif
348 :
349 8 : }
350 :
351 :
352 7 : static void build_shape_elevation_grid(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state)
353 : {
354 7 : mesh_new_elevation_grid(stack->mesh, n);
355 7 : }
356 :
357 228 : static void TraverseElevationGrid(GF_Node *n, void *rs, Bool is_destroy)
358 : {
359 228 : drawable_3d_base_traverse(n, rs, is_destroy, build_shape_elevation_grid);
360 228 : }
361 :
362 0 : static void ElevationGrid_SetHeight(GF_Node *node, GF_Route *route)
363 : {
364 : M_ElevationGrid *eg = (M_ElevationGrid *)node;
365 0 : if (node) {
366 0 : gf_sg_vrml_field_copy(&eg->height, &eg->set_height, GF_SG_VRML_MFFLOAT);
367 0 : gf_sg_vrml_mf_reset(&eg->set_height, GF_SG_VRML_MFFLOAT);
368 : }
369 0 : }
370 :
371 7 : void compositor_init_elevation_grid(GF_Compositor *compositor, GF_Node *node)
372 : {
373 : M_ElevationGrid *eg = (M_ElevationGrid *)node;
374 7 : drawable_3d_new(node);
375 7 : gf_node_set_callback_function(node, TraverseElevationGrid);
376 7 : eg->on_set_height = ElevationGrid_SetHeight;
377 : #ifdef GPAC_ENABLE_COVERAGE
378 7 : if (gf_sys_is_cov_mode()) {
379 : ElevationGrid_SetHeight(NULL, NULL);
380 : }
381 : #endif
382 7 : }
383 :
384 7 : static void build_shape_extrusion(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state)
385 : {
386 7 : mesh_new_extrusion(stack->mesh, n);
387 7 : }
388 :
389 328 : static void TraverseExtrusion(GF_Node *n, void *rs, Bool is_destroy)
390 : {
391 328 : drawable_3d_base_traverse(n, rs, is_destroy, build_shape_extrusion);
392 328 : }
393 :
394 0 : static void Extrusion_SetCrossSection(GF_Node *node, GF_Route *route)
395 : {
396 : M_Extrusion *eg = (M_Extrusion *)node;
397 0 : if (node) {
398 0 : gf_sg_vrml_field_copy(&eg->crossSection, &eg->set_crossSection, GF_SG_VRML_MFVEC2F);
399 0 : gf_sg_vrml_mf_reset(&eg->set_crossSection, GF_SG_VRML_MFVEC2F);
400 : }
401 0 : }
402 0 : static void Extrusion_SetOrientation(GF_Node *node, GF_Route *route)
403 : {
404 : M_Extrusion *eg = (M_Extrusion *)node;
405 0 : if (node) {
406 0 : gf_sg_vrml_field_copy(&eg->orientation, &eg->set_orientation, GF_SG_VRML_MFROTATION);
407 0 : gf_sg_vrml_mf_reset(&eg->set_orientation, GF_SG_VRML_MFROTATION);
408 : }
409 0 : }
410 0 : static void Extrusion_SetScale(GF_Node *node, GF_Route *route)
411 : {
412 : M_Extrusion *eg = (M_Extrusion *)node;
413 0 : if (node) {
414 0 : gf_sg_vrml_field_copy(&eg->scale, &eg->set_scale, GF_SG_VRML_MFVEC2F);
415 0 : gf_sg_vrml_mf_reset(&eg->set_scale, GF_SG_VRML_MFVEC2F);
416 : }
417 0 : }
418 0 : static void Extrusion_SetSpine(GF_Node *node, GF_Route *route)
419 : {
420 : M_Extrusion *eg = (M_Extrusion *)node;
421 0 : if (node) {
422 0 : gf_sg_vrml_field_copy(&eg->spine, &eg->set_spine, GF_SG_VRML_MFVEC3F);
423 0 : gf_sg_vrml_mf_reset(&eg->set_spine, GF_SG_VRML_MFVEC3F);
424 : }
425 0 : }
426 7 : void compositor_init_extrusion(GF_Compositor *compositor, GF_Node *node)
427 : {
428 : M_Extrusion *ext = (M_Extrusion *)node;
429 7 : drawable_3d_new(node);
430 7 : gf_node_set_callback_function(node, TraverseExtrusion);
431 7 : ext->on_set_crossSection = Extrusion_SetCrossSection;
432 7 : ext->on_set_orientation = Extrusion_SetOrientation;
433 7 : ext->on_set_scale = Extrusion_SetScale;
434 7 : ext->on_set_spine = Extrusion_SetSpine;
435 :
436 : #ifdef GPAC_ENABLE_COVERAGE
437 7 : if (gf_sys_is_cov_mode()) {
438 : Extrusion_SetCrossSection(NULL, NULL);
439 : Extrusion_SetOrientation(NULL, NULL);
440 : Extrusion_SetScale(NULL, NULL);
441 : Extrusion_SetSpine(NULL, NULL);
442 : }
443 : #endif
444 :
445 7 : }
446 :
447 :
448 : /*
449 : NonLinearDeformer
450 :
451 : NOTE: AFX spec is just hmm, well, hmm. NonLinearDeformer.extend interpretation differ from type to
452 : type within the spec, and between the spec and the ref soft. This is GPAC interpretation
453 : * all params are specified with default transform axis (Z axis)
454 : * NLD.type = 0 (taper):
455 : * taping radius = NLD.param
456 : * extend = N * [diff, perc] with
457 : - diff : relative position along taper axis (for default, diff=0: z min, diff=1: z max)
458 : - perc: mult ratio for base taper radius
459 : extend works like key/keyValue for a scalar interpolator
460 : final radius: r(z) = LinearInterp(extend[min key], extend[min key + 1]) * param
461 :
462 : * NLD.type = 1 (twister):
463 : * twisting angle = NLD.param
464 : * extend = N * [diff, perc] with
465 : - diff : relative position along twister axis (for default, diff=0: z min, diff=1: z max)
466 : - perc: mult ratio for base twister angle
467 : extend works like key/keyValue for a scalar interpolator
468 : final angle: a(z) = LinearInterp(extend[min key], extend[min key + 1]) * param
469 :
470 : * NLD.type = 2 (bender):
471 : * bending curvature = NLD.param
472 : * extend = N * [diff, perc] with
473 : - diff : relative position along bender axis (for default, diff=0: z min, diff=1: z max)
474 : - perc: mult ratio for base bender curvature
475 : extend works like key/keyValue for a scalar interpolator
476 : final curvature: c(z) = LinearInterp(extend[min key], extend[min key + 1]) * param
477 :
478 : Another pb of NLD is that the spec says nothing about object/axis alignment: should we center
479 : the object at 0,0,0 (local coords) or not? the results are quite different. Here we don't
480 : recenter the object before transform
481 :
482 :
483 : TODO - think of a way to implement this through the vertex shader
484 : */
485 :
486 156 : static Bool NLD_GetMatrix(M_NonLinearDeformer *nld, GF_Matrix *mx)
487 : {
488 : SFVec3f v1, v2;
489 : SFRotation r;
490 : Fixed l1, l2, dot;
491 :
492 : /*compute rotation matrix from NLD axis to 0 0 1*/
493 156 : v1 = nld->axis;
494 156 : gf_vec_norm(&v1);
495 : v2.x = v2.y = 0;
496 : v2.z = FIX_ONE;
497 156 : if (gf_vec_equal(v1, v2)) return 0;
498 :
499 156 : l1 = gf_vec_len(v1);
500 156 : l2 = gf_vec_len(v2);
501 156 : dot = gf_divfix(gf_vec_dot(v1, v2), gf_mulfix(l1, l2));
502 :
503 156 : r.x = gf_mulfix(v1.y, v2.z) - gf_mulfix(v2.y, v1.z);
504 156 : r.y = gf_mulfix(v1.z, v2.x) - gf_mulfix(v2.z, v1.x);
505 156 : r.z = gf_mulfix(v1.x, v2.y) - gf_mulfix(v2.x, v1.y);
506 156 : r.q = gf_atan2(gf_sqrt(FIX_ONE - gf_mulfix(dot, dot)), dot);
507 312 : gf_mx_init(*mx);
508 156 : gf_mx_add_rotation(mx, r.q, r.x, r.y, r.z);
509 156 : return 1;
510 : }
511 :
512 563400 : static GFINLINE void NLD_GetKey(M_NonLinearDeformer *nld, Fixed frac, Fixed *f_min, Fixed *min, Fixed *f_max, Fixed *max)
513 : {
514 : u32 i, count;
515 563400 : count = nld->extend.count;
516 563400 : if (count%2) count--;
517 :
518 563400 : *f_min = 0;
519 563400 : *min = 0;
520 980400 : for (i=0; i<count; i+=2) {
521 980400 : if (frac>=nld->extend.vals[i]) {
522 980400 : *f_min = nld->extend.vals[i];
523 980400 : *min = nld->extend.vals[i+1];
524 : }
525 980400 : if ((i+2<count) && (frac<=nld->extend.vals[i+2])) {
526 563400 : *f_max = nld->extend.vals[i+2];
527 563400 : *max = nld->extend.vals[i+3];
528 : return;
529 : }
530 : }
531 0 : if (count) {
532 0 : *f_max = nld->extend.vals[count-2];
533 0 : *max = nld->extend.vals[count-1];
534 : } else {
535 0 : *f_max = FIX_ONE;
536 0 : *max = GF_PI;
537 : }
538 : }
539 :
540 156 : static void NLD_Apply(M_NonLinearDeformer *nld, GF_Mesh *mesh)
541 : {
542 : u32 i;
543 : GF_Matrix mx;
544 : SFVec3f n;
545 : Fixed param, z_min, z_max, v_min, v_max, f_min, f_max, frac, val, a_cos, a_sin;
546 156 : Bool needs_transform = NLD_GetMatrix(nld, &mx);
547 :
548 156 : param = nld->param;
549 156 : if (!param) param = 1;
550 :
551 162 : if (mesh->bounds.min_edge.z == mesh->bounds.max_edge.z) return;
552 :
553 : z_min = FIX_MAX;
554 : z_max = -FIX_MAX;
555 150 : if (needs_transform) {
556 563400 : for (i=0; i<mesh->v_count; i++) {
557 563400 : gf_mx_apply_vec(&mx, &mesh->vertices[i].pos);
558 563400 : MESH_GET_NORMAL(n, mesh->vertices[i]);
559 563400 : gf_mx_rotate_vector(&mx, &n);
560 563400 : MESH_SET_NORMAL(mesh->vertices[i], n);
561 :
562 563400 : if (mesh->vertices[i].pos.z<z_min) z_min = mesh->vertices[i].pos.z;
563 563400 : if (mesh->vertices[i].pos.z>z_max) z_max = mesh->vertices[i].pos.z;
564 : }
565 : } else {
566 : z_min = mesh->bounds.min_edge.z;
567 : z_max = mesh->bounds.max_edge.z;
568 : }
569 :
570 563550 : for (i=0; i<mesh->v_count; i++) {
571 563400 : SFVec3f old = mesh->vertices[i].pos;
572 563400 : frac = gf_divfix(old.z - z_min, z_max - z_min);
573 563400 : NLD_GetKey(nld, frac, &f_min, &v_min, &f_max, &v_max);
574 563400 : if (f_max == f_min) {
575 0 : val = v_min;
576 : } else {
577 563400 : frac = gf_divfix(frac-f_min, f_max - f_min);
578 563400 : val = gf_mulfix(v_max - v_min, frac) + v_min;
579 : }
580 563400 : val = gf_mulfix(val, param);
581 :
582 563400 : switch (nld->type) {
583 : /*taper*/
584 563400 : case 0:
585 563400 : mesh->vertices[i].pos.x = gf_mulfix(mesh->vertices[i].pos.x, val);
586 563400 : mesh->vertices[i].pos.y = gf_mulfix(mesh->vertices[i].pos.y, val);
587 563400 : MESH_GET_NORMAL(old, mesh->vertices[i]);
588 563400 : n=old;
589 563400 : n.x = gf_mulfix(n.x, val);
590 563400 : n.y = gf_mulfix(n.y, val);
591 563400 : gf_vec_norm(&n);
592 563400 : MESH_SET_NORMAL(mesh->vertices[i], n);
593 563400 : break;
594 : /*twist*/
595 0 : case 1:
596 0 : a_cos = gf_cos(val);
597 0 : a_sin = gf_sin(val);
598 0 : mesh->vertices[i].pos.x = gf_mulfix(a_cos, old.x) - gf_mulfix(a_sin, old.y);
599 0 : mesh->vertices[i].pos.y = gf_mulfix(a_sin, old.x) + gf_mulfix(a_cos, old.y);
600 0 : MESH_GET_NORMAL(old, mesh->vertices[i]);
601 0 : n=old;
602 0 : n.x = gf_mulfix(a_cos, old.x) - gf_mulfix(a_sin, old.y);
603 0 : n.y = gf_mulfix(a_sin, old.x) + gf_mulfix(a_cos, old.y);
604 0 : gf_vec_norm(&n);
605 0 : MESH_SET_NORMAL(mesh->vertices[i], n);
606 0 : break;
607 : /*bend*/
608 0 : case 2:
609 0 : a_cos = gf_cos(val);
610 0 : a_sin = gf_sin(val);
611 0 : mesh->vertices[i].pos.x = gf_mulfix(a_sin, old.z) + gf_mulfix(a_cos, old.x);
612 0 : mesh->vertices[i].pos.z = gf_mulfix(a_cos, old.z) - gf_mulfix(a_sin, old.x);
613 0 : MESH_GET_NORMAL(old, mesh->vertices[i]);
614 0 : n=old;
615 0 : n.x = gf_mulfix(a_sin, old.z) + gf_mulfix(a_cos, old.x);
616 0 : n.z = gf_mulfix(a_cos, old.z) - gf_mulfix(a_sin, old.x);
617 0 : gf_vec_norm(&n);
618 0 : MESH_SET_NORMAL(mesh->vertices[i], n);
619 0 : break;
620 : /*pinch, not standard (taper on X dim only)*/
621 0 : case 3:
622 0 : mesh->vertices[i].pos.x = gf_mulfix(mesh->vertices[i].pos.x, val);
623 0 : MESH_GET_NORMAL(old, mesh->vertices[i]);
624 0 : n=old;
625 0 : n.x = gf_mulfix(n.x, val);
626 0 : gf_vec_norm(&n);
627 0 : MESH_SET_NORMAL(mesh->vertices[i], n);
628 0 : break;
629 : }
630 : }
631 150 : if (needs_transform) {
632 150 : gf_mx_inverse(&mx);
633 563550 : for (i=0; i<mesh->v_count; i++) {
634 563400 : gf_mx_apply_vec(&mx, &mesh->vertices[i].pos);
635 563400 : MESH_GET_NORMAL(n, mesh->vertices[i]);
636 563400 : gf_mx_rotate_vector(&mx, &n);
637 563400 : MESH_SET_NORMAL(mesh->vertices[i], n);
638 : }
639 : }
640 150 : mesh_update_bounds(mesh);
641 150 : gf_mesh_build_aabbtree(mesh);
642 : }
643 :
644 156 : static void build_shape_nld(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state)
645 : {
646 : M_NonLinearDeformer *nld = (M_NonLinearDeformer*)n;
647 156 : Drawable3D *geo_st = (Drawable3D *)gf_node_get_private(nld->geometry);
648 :
649 156 : if (!nld->geometry) return;
650 156 : if (!geo_st) return;
651 :
652 156 : mesh_clone(stack->mesh, geo_st->mesh);
653 : /*apply deforms*/
654 156 : NLD_Apply(nld, stack->mesh);
655 : }
656 :
657 454 : static void TraverseNonLinearDeformer(GF_Node *n, void *rs, Bool is_destroy)
658 : {
659 : GF_TraverseState *tr_state = (GF_TraverseState *)rs;
660 :
661 : /*traverse geometry for get_bounds to make sure geometry is up to date*/
662 454 : if (!is_destroy && (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS)) {
663 150 : gf_node_traverse(((M_NonLinearDeformer*)n)->geometry, tr_state);
664 : }
665 :
666 454 : drawable_3d_base_traverse(n, rs, is_destroy, build_shape_nld);
667 454 : }
668 :
669 7 : void compositor_init_non_linear_deformer(GF_Compositor *compositor, GF_Node *node)
670 : {
671 7 : drawable_3d_new(node);
672 7 : gf_node_set_callback_function(node, TraverseNonLinearDeformer);
673 7 : }
674 :
675 : #endif /*GPAC_DISABLE_3D*/
676 :
677 : #endif /*GPAC_DISABLE_VRML*/
|