1 /*
2 * Tux Racer
3 * Copyright (C) 1999-2001 Jasmin F. Patry
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 #include "tuxracer.h"
21 #include "hier.h"
22 #include "hier_util.h"
23 #include "alglib.h"
24
25 /*
26 * These hash tables map from names to node pointers and material data, resp.
27 */
28 Tcl_HashTable g_hier_node_table;
29 Tcl_HashTable g_hier_material_table;
30
31
32 /*
33 * State variables
34 */
35
36
37 /* Default Material */
38 material_t g_hier_default_material =
39 { { 0., 0., 1., 1.0 }, /* diffuse colour = blue */
40 { 0., 0., 0., 1.0 }, /* specular colour = black */
41 0.0 /* specular exp. = 0.0 */
42 };
43
44
45 /*
46 * Functions used to access and update the name-to-pointer hash tables
47 */
48
49 /* Add a new node name to the node name hash table.
50 node_name contains the child's name only. */
51 static int
add_scene_node(char * parent_name,char * node_name,scene_node_t * node)52 add_scene_node( char *parent_name, char *node_name, scene_node_t *node )
53 {
54 Tcl_HashEntry *entry;
55 int newEntry;
56 char new_name[1024];
57
58 /* Add the current node to the hash table
59 */
60 if (0 == strcmp(parent_name,":")) {
61 sprintf(new_name, ":%s", node_name);
62 } else {
63 sprintf(new_name, "%s:%s", parent_name, node_name);
64 }
65
66 node->name = (char*)malloc( strlen(new_name) + 1 );
67 node->name = strcpy(node->name, new_name);
68
69 entry = Tcl_CreateHashEntry(&g_hier_node_table,new_name,&newEntry);
70
71 if (newEntry) {
72 Tcl_SetHashValue(entry, node);
73 } else {
74 return TCL_ERROR;
75 }
76
77 return TCL_OK;
78 }
79
80 /* Get node pointer from node name */
81 int
get_scene_node(char * node_name,scene_node_t ** node)82 get_scene_node( char *node_name, scene_node_t **node )
83 {
84 Tcl_HashEntry *entry;
85
86 entry = Tcl_FindHashEntry(&g_hier_node_table, node_name);
87 if (0 == entry) {
88 if(0 == strcmp(node_name, ":")) {
89 /* the root has been specified
90 */
91 *node = 0;
92 } else {
93 return TCL_ERROR;
94 }
95 } else {
96 *node = (scene_node_t*)Tcl_GetHashValue(entry);
97 }
98
99 return TCL_OK;
100 }
101
102 /* Add a new material name to the material name hash table. */
103 int
add_material(char * mat_name,material_t * mat)104 add_material( char *mat_name, material_t *mat )
105 {
106 Tcl_HashEntry *entry;
107 int newEntry;
108
109 entry = Tcl_CreateHashEntry(&g_hier_material_table,mat_name,&newEntry);
110 if (newEntry) {
111 Tcl_SetHashValue(entry, mat);
112 } else {
113 return TCL_ERROR;
114 }
115
116 return TCL_OK;
117 }
118
119 /* Get material pointer from material name */
120 int
get_material(char * mat_name,material_t ** mat)121 get_material( char *mat_name, material_t **mat )
122 {
123 Tcl_HashEntry *entry;
124
125 entry = Tcl_FindHashEntry(&g_hier_material_table, mat_name);
126 if (0 == entry) {
127 return TCL_ERROR;
128 } else {
129 *mat = (material_t*)Tcl_GetHashValue(entry);
130 }
131
132 return TCL_OK;
133 }
134
135 /* Creates a new node, add the node to the hash table, and inserts the
136 node into the DAG. Default values are given to all fields except
137 the type-specific ones (geom, param). */
create_scene_node(char * parent_name,char * child_name,scene_node_t ** node)138 char* create_scene_node( char *parent_name, char *child_name,
139 scene_node_t **node )
140 {
141 scene_node_t *parent, *child;
142 if ( get_scene_node( parent_name, &parent ) != TCL_OK ) {
143 return "Parent node does not exist";
144 }
145
146 /* Create node */
147 child = (scene_node_t *)malloc( sizeof( scene_node_t ) );
148
149 /* Initialize node */
150 child->parent = parent;
151 child->next = NULL;
152 child->child = NULL;
153 child->mat = NULL;
154 child->render_shadow = True;
155 child->eye = False;
156 make_identity_matrix( child->trans );
157 make_identity_matrix( child->invtrans );
158
159 if ( add_scene_node( parent_name, child_name, child ) != TCL_OK ) {
160 free( child );
161 return "Child already exists";
162 }
163
164
165 /* Add node to parent's children */
166 if ( parent != NULL ) {
167 if ( parent->child == NULL ) {
168 parent->child = child;
169 } else {
170 for (parent = parent->child; parent->next != NULL;
171 parent = parent->next) {/* do nothing */}
172 parent->next = child;
173 }
174 }
175
176 *node = child;
177 return NULL;
178 }
179
180 char*
reset_scene_node(char * node)181 reset_scene_node( char *node )
182 {
183 scene_node_t *nodePtr;
184
185 if ( get_scene_node( node, &nodePtr ) != TCL_OK ) {
186 return "No such node";
187 }
188
189 make_identity_matrix( nodePtr->trans );
190 make_identity_matrix( nodePtr->invtrans );
191
192 return NULL;
193 }
194
195 char*
rotate_scene_node(char * node,char axis,scalar_t angle)196 rotate_scene_node( char *node, char axis, scalar_t angle )
197 {
198 scene_node_t *nodePtr;
199 matrixgl_t rotMatrix;
200
201 if ( get_scene_node( node, &nodePtr ) != TCL_OK ) {
202 return "No such node";
203 }
204
205 make_rotation_matrix( rotMatrix, angle, axis );
206 multiply_matrices( nodePtr->trans, nodePtr->trans, rotMatrix );
207 make_rotation_matrix( rotMatrix, -angle, axis );
208 multiply_matrices( nodePtr->invtrans, rotMatrix, nodePtr->invtrans );
209
210 return NULL;
211 }
212
213 char*
translate_scene_node(char * node,vector_t vec)214 translate_scene_node( char *node, vector_t vec )
215 {
216 scene_node_t *nodePtr;
217 matrixgl_t xlateMatrix;
218
219 if ( get_scene_node( node, &nodePtr ) != TCL_OK ) {
220 return "No such node";
221 }
222
223 make_translation_matrix( xlateMatrix, vec.x, vec.y, vec.z );
224 multiply_matrices( nodePtr->trans, nodePtr->trans, xlateMatrix );
225 make_translation_matrix( xlateMatrix, -vec.x, -vec.y, -vec.z );
226 multiply_matrices( nodePtr->invtrans, xlateMatrix, nodePtr->invtrans );
227
228 return NULL;
229 }
230
231 char*
scale_scene_node(char * node,point_t center,scalar_t factor[3])232 scale_scene_node( char *node, point_t center, scalar_t factor[3] )
233 {
234 scene_node_t *nodePtr;
235 matrixgl_t matrix;
236
237 if ( get_scene_node( node, &nodePtr ) != TCL_OK ) {
238 return "No such node";
239 }
240
241 make_translation_matrix( matrix, -center.x, -center.y, -center.z );
242 multiply_matrices( nodePtr->trans, nodePtr->trans, matrix );
243 make_translation_matrix( matrix, center.x, center.y, center.z );
244 multiply_matrices( nodePtr->invtrans, matrix, nodePtr->invtrans );
245
246 make_scaling_matrix( matrix, factor[0], factor[1], factor[2] );
247 multiply_matrices( nodePtr->trans, nodePtr->trans, matrix );
248 make_scaling_matrix( matrix, 1./factor[0], 1./factor[1], 1./factor[2] );
249 multiply_matrices( nodePtr->invtrans, matrix, nodePtr->invtrans );
250
251 make_translation_matrix( matrix, center.x, center.y, center.z );
252 multiply_matrices( nodePtr->trans, nodePtr->trans, matrix );
253 make_translation_matrix( matrix, -center.x, -center.y, -center.z );
254 multiply_matrices( nodePtr->invtrans, matrix, nodePtr->invtrans );
255
256 return NULL;
257 }
258
259 char*
transform_scene_node(char * node,matrixgl_t mat,matrixgl_t invmat)260 transform_scene_node( char *node, matrixgl_t mat, matrixgl_t invmat )
261 {
262 scene_node_t *nodePtr;
263
264 if ( get_scene_node( node, &nodePtr ) != TCL_OK ) {
265 return "No such node";
266 }
267
268 multiply_matrices( nodePtr->trans, nodePtr->trans, mat );
269 multiply_matrices( nodePtr->invtrans, invmat, nodePtr->invtrans );
270
271 return NULL;
272 }
273
274 char*
set_scene_node_material(char * node,char * mat)275 set_scene_node_material( char *node, char *mat )
276 {
277 material_t *matPtr;
278 scene_node_t *nodePtr;
279
280 if ( get_scene_node( node, &nodePtr ) != TCL_OK ) {
281 return "No such node";
282 }
283
284 if ( get_material( mat, &matPtr ) != TCL_OK ) {
285 return "No such material";
286 }
287
288 nodePtr->mat = matPtr;
289
290 return NULL;
291 }
292
293 char*
set_scene_node_shadow_state(char * node,char * state)294 set_scene_node_shadow_state( char *node, char *state )
295 {
296 scene_node_t *nodePtr;
297
298 if ( get_scene_node( node, &nodePtr ) != TCL_OK ) {
299 return "No such node";
300 }
301
302 if ( strcmp( state, "off" ) == 0 ) {
303 nodePtr->render_shadow = False;
304 } else if ( strcmp ( state, "on" ) == 0 ) {
305 nodePtr->render_shadow = True;
306 } else {
307 return "Shadow state must be 'on' or 'off'";
308 }
309
310 return NULL;
311 }
312
313 char*
set_scene_node_eye(char * node,char * which_eye)314 set_scene_node_eye( char *node, char *which_eye )
315 {
316 scene_node_t *nodePtr;
317
318 if ( get_scene_node( node, &nodePtr ) != TCL_OK ) {
319 return "No such node";
320 }
321
322 if ( strcmp( which_eye, "right" ) == 0 ) {
323 nodePtr->eye = True;
324 nodePtr->which_eye = TuxRightEye;
325 } else if ( strcmp ( which_eye, "left" ) == 0 ) {
326 nodePtr->eye = True;
327 nodePtr->which_eye = TuxLeftEye;
328 } else {
329 return "'eye' must be right or left";
330 }
331
332 return NULL;
333 }
334
335 char*
create_tranform_node(char * parent_name,char * child_name)336 create_tranform_node( char *parent_name, char *child_name )
337 {
338 scene_node_t *node;
339 char *msg;
340
341 msg = create_scene_node(parent_name, child_name, &node);
342 if ( msg != NULL ) {
343 return msg;
344 }
345
346 node->geom = Empty;
347
348 return NULL;
349 }
350
351 char*
create_sphere_node(char * parent_name,char * child_name,scalar_t resolution)352 create_sphere_node( char *parent_name, char *child_name, scalar_t resolution )
353 {
354 scene_node_t *node;
355 char *msg;
356
357 msg = create_scene_node(parent_name, child_name, &node);
358 if ( msg != NULL ) {
359 return msg;
360 }
361
362 node->geom = Sphere;
363 node->param.sphere.radius = 1.0;
364 node->param.sphere.divisions = min(
365 MAX_SPHERE_DIVISIONS, max(
366 MIN_SPHERE_DIVISIONS,
367 ROUND_TO_NEAREST( getparam_tux_sphere_divisions() * resolution )
368 ) );
369
370 return NULL;
371 }
372
373 char*
create_material(char * mat,colour_t diffuse,colour_t specular_colour,scalar_t specular_exp)374 create_material( char *mat, colour_t diffuse,
375 colour_t specular_colour, scalar_t specular_exp )
376 {
377 material_t *matPtr;
378
379 matPtr = (material_t *)malloc( sizeof( material_t ) );
380
381 matPtr->diffuse.r = diffuse.r;
382 matPtr->diffuse.g = diffuse.g;
383 matPtr->diffuse.b = diffuse.b;
384 matPtr->diffuse.a = 1.0;
385
386 matPtr->specular_colour.r = specular_colour.r;
387 matPtr->specular_colour.g = specular_colour.g;
388 matPtr->specular_colour.b = specular_colour.b;
389 matPtr->specular_colour.a = 1.0;
390
391 matPtr->specular_exp = specular_exp;
392
393 if ( add_material( mat, matPtr ) != TCL_OK ) {
394 free( matPtr );
395 return "Material already exists";
396 }
397
398 return NULL;
399 }
400
initialize_scene_graph()401 void initialize_scene_graph()
402 {
403 /* Initialize state */
404
405 g_hier_default_material.diffuse.r = 0.0;
406 g_hier_default_material.diffuse.g = 0.0;
407 g_hier_default_material.diffuse.b = 1.0;
408 g_hier_default_material.diffuse.a = 1.0;
409
410 g_hier_default_material.specular_colour.r = 0.0;
411 g_hier_default_material.specular_colour.g = 0.0;
412 g_hier_default_material.specular_colour.b = 0.0;
413 g_hier_default_material.specular_colour.a = 1.0;
414
415 g_hier_default_material.specular_exp = 0.0;
416
417 Tcl_InitHashTable(&g_hier_node_table,TCL_STRING_KEYS);
418 Tcl_InitHashTable(&g_hier_material_table,TCL_STRING_KEYS);
419 }
420
draw_scene_graph(char * node)421 void draw_scene_graph( char *node )
422 {
423 scene_node_t *nodePtr;
424
425 if ( get_scene_node( node, &nodePtr ) != TCL_OK ) {
426 handle_error( 1, "draw_scene_graph: No such node `%s'", node );
427 }
428
429 traverse_dag( nodePtr, &g_hier_default_material );
430
431 }
432
collide(char * node,polyhedron_t ph)433 bool_t collide( char *node, polyhedron_t ph )
434 {
435 scene_node_t *nodePtr;
436 matrixgl_t mat, invmat;
437
438 make_identity_matrix( mat );
439 make_identity_matrix( invmat );
440
441 if ( get_scene_node( node, &nodePtr ) != TCL_OK ) {
442 handle_error( 1, "draw_scene_graph: No such node `%s'", node );
443 }
444
445 return check_polyhedron_collision_with_dag( nodePtr, mat, invmat, ph );
446
447 }
448
449