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