| |
1 #include <assert.h> |
| |
2 #include <GL/gl.h> |
| |
3 #include <GL/glu.h> |
| |
4 #include "cc_interface.h" |
| |
5 #include "crazychat.h" |
| |
6 |
| |
7 #include "Utilities.h" |
| |
8 #include "QTUtilities.h" |
| |
9 #include "camdata.h" |
| |
10 #include "camproc.h" |
| |
11 #include "util.h" |
| |
12 #include <unistd.h> |
| |
13 |
| |
14 |
| |
15 #ifdef __APPLE_CC__ |
| |
16 #include <Carbon/Carbon.h> |
| |
17 #else |
| |
18 #include <Carbon.h> |
| |
19 #endif |
| |
20 |
| |
21 /* temporarily including for development testing */ |
| |
22 #include "cc_gtk_gl.h" |
| |
23 |
| |
24 /* hard coding the webcam dimensions: BAD, but we're not probing hardware yet */ |
| |
25 |
| |
26 #define WEBCAM_X 644 /* webcam's x dim */ |
| |
27 #define WEBCAM_Y 480 /* webcam's y dim */ |
| |
28 |
| |
29 /* default webcam timer callback delay */ |
| |
30 #define WEBCAM_CALLBACK_DELAY 40 /* in ms */ |
| |
31 |
| |
32 int x_click, y_click; |
| |
33 int mode_change=0; |
| |
34 |
| |
35 struct input_instance input_data; |
| |
36 |
| |
37 /* move this to input_instance eventually */ |
| |
38 /* |
| |
39 UInt32 colorBuf[WEBCAM_Y][WEBCAM_X]; |
| |
40 */ |
| |
41 unsigned int (*colorBuf)[640]; |
| |
42 int detection_mode=0; |
| |
43 int draw_mode=0; //0=pixels, 1=face |
| |
44 int easter_count; |
| |
45 static void *kickstart(void *data); |
| |
46 static void *facefind(void *data); |
| |
47 |
| |
48 /** |
| |
49 * Resets the OpenGL viewport stuff on widget reconfiguration (resize, |
| |
50 * reposition) |
| |
51 * @param widget widget that got reconfigured |
| |
52 * @param event the configuration event |
| |
53 * @param data unused |
| |
54 * @return TRUE ( i don't know what FALSE would do ) |
| |
55 */ |
| |
56 static gboolean config_wrapper(GtkWidget *widget, GdkEventConfigure *event, |
| |
57 void *data); |
| |
58 |
| |
59 /** |
| |
60 * Debug features test. Draws pixels directly to the frame buffer. |
| |
61 * @param widget widget that we're drawing |
| |
62 * @param event the draw event |
| |
63 * @param data array of pixels |
| |
64 * @return DUNNO |
| |
65 */ |
| |
66 static gboolean mydraw(GtkWidget *widget, GdkEventExpose *event, |
| |
67 void *data); |
| |
68 |
| |
69 /** |
| |
70 * Periodically querys the webcam for more data. |
| |
71 * @param instance webcam input instance data |
| |
72 * @return TRUE to stop other handler, FALSE to continue |
| |
73 */ |
| |
74 static gboolean webcam_cb(struct input_instance *instance); |
| |
75 |
| |
76 /** |
| |
77 * Init window code, adding our click callback. |
| |
78 * @param widget the window we clicked in |
| |
79 * @param instance webcam input instance data |
| |
80 */ |
| |
81 static void init_cb(GtkWidget *widget, struct input_instance *instance); |
| |
82 |
| |
83 /** |
| |
84 * Click callback |
| |
85 * @param widget the window we clicked in |
| |
86 * @param event the button click event structure |
| |
87 * @param instance input instance data |
| |
88 * @return TRUE to stop other handler, FALSE to continue |
| |
89 */ |
| |
90 static gboolean click_cb(GtkWidget *widget, GdkEventButton *event, |
| |
91 struct input_instance *instance); |
| |
92 |
| |
93 /** |
| |
94 * Button callback |
| |
95 * @param button the button we clicked on |
| |
96 * @param instance input instance data |
| |
97 */ |
| |
98 static void button_cb(GtkWidget *button, struct input_instance *instance); |
| |
99 |
| |
100 /** |
| |
101 * Destroy callback. Called when the input processing window is destroyed. |
| |
102 * @param widget the window we clicked in |
| |
103 * @param cc crazychat global data structure |
| |
104 */ |
| |
105 static void destroy_cb(GtkWidget *widget, struct crazychat *cc); |
| |
106 |
| |
107 /** |
| |
108 * Set feature material. |
| |
109 * @param entry model selector combo box entry |
| |
110 * @param material pointer to material we're setting |
| |
111 */ |
| |
112 static void material_set(GtkWidget *entry, guint8 *material); |
| |
113 |
| |
114 struct input_instance *init_input(struct crazychat *cc) |
| |
115 { |
| |
116 |
| |
117 /*pthread_t userinput_t; // should we put this in a nicer wrapper?*/ |
| |
118 struct draw_info *info; |
| |
119 struct input_instance *instance; |
| |
120 info = (struct draw_info*)malloc(sizeof(*info)); |
| |
121 assert(info); |
| |
122 memset(info, 0, sizeof(*info)); |
| |
123 info->timeout = TRUE; |
| |
124 info->delay_ms = DEFAULT_FRAME_DELAY; |
| |
125 info->data = &input_data; |
| |
126 instance = (struct input_instance*)info->data; |
| |
127 memset(instance, 0, sizeof(*instance)); |
| |
128 instance->output.features = &instance->face; |
| |
129 EnterMovies(); |
| |
130 filter_bank *bank; |
| |
131 bank = Filter_Initialize(); |
| |
132 assert(CamProc(instance, bank) == noErr); // change this prototype-> no windows |
| |
133 instance->timer_id = g_timeout_add(WEBCAM_CALLBACK_DELAY, |
| |
134 (GSourceFunc)webcam_cb, instance); |
| |
135 /* THREAD_CREATE(&userinput_t, facefind, instance); // is this being created correctly?*/ |
| |
136 struct window_box ret; |
| |
137 cc_new_gl_window(init_cb, config_wrapper, mydraw, |
| |
138 info, &ret); |
| |
139 instance->widget = ret.window; |
| |
140 gtk_window_set_title(GTK_WINDOW(ret.window), "Local User"); |
| |
141 instance->box = ret.vbox; |
| |
142 GtkWidget *label = gtk_label_new("Click your face"); |
| |
143 instance->label = label; |
| |
144 gtk_box_pack_start(GTK_BOX(ret.vbox), label, FALSE, FALSE, 0); |
| |
145 gtk_box_reorder_child(GTK_BOX(ret.vbox), label, 0); |
| |
146 gtk_widget_show(label); |
| |
147 GtkWidget *button = gtk_button_new_with_label("Confirm"); |
| |
148 gtk_box_pack_start(GTK_BOX(ret.vbox), button, FALSE, FALSE, 0); |
| |
149 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_cb), |
| |
150 instance); |
| |
151 instance->button = button; |
| |
152 gtk_widget_show(button); |
| |
153 |
| |
154 GtkWidget *hbox = gtk_hbox_new(TRUE, 0); |
| |
155 gtk_box_pack_start(GTK_BOX(ret.vbox), hbox, FALSE, FALSE, 0); |
| |
156 gtk_widget_show(hbox); |
| |
157 |
| |
158 GtkWidget *model_combo = gtk_combo_new(); |
| |
159 GList *glist = NULL; |
| |
160 |
| |
161 glist = g_list_append(glist, "Dog"); |
| |
162 glist = g_list_append(glist, "Shark"); |
| |
163 gtk_combo_set_popdown_strings(GTK_COMBO(model_combo), glist); |
| |
164 g_list_free(glist); |
| |
165 //gtk_combo_box_set_column_span_column(GTK_COMBO(model_combo), |
| |
166 // 10); |
| |
167 gtk_box_pack_start(GTK_BOX(hbox), model_combo, TRUE, TRUE, 0); |
| |
168 gtk_widget_show(model_combo); |
| |
169 instance->model = model_combo; |
| |
170 |
| |
171 GtkWidget *head_material_combo = gtk_combo_new(); |
| |
172 glist = NULL; |
| |
173 glist = g_list_append(glist, "Red"); |
| |
174 glist = g_list_append(glist, "Dark Brown"); |
| |
175 glist = g_list_append(glist, "Light Brown"); |
| |
176 glist = g_list_append(glist, "White"); |
| |
177 glist = g_list_append(glist, "Green"); |
| |
178 glist = g_list_append(glist, "Black"); |
| |
179 gtk_combo_set_popdown_strings(GTK_COMBO(head_material_combo), glist); |
| |
180 g_list_free(glist); |
| |
181 //gtk_combo_box_set_column_span_column(GTK_COMBO(head_material_combo), |
| |
182 // 10); |
| |
183 gtk_box_pack_start(GTK_BOX(hbox), head_material_combo, TRUE, TRUE, 0); |
| |
184 gtk_widget_show(head_material_combo); |
| |
185 instance->head = head_material_combo; |
| |
186 |
| |
187 GtkWidget *appendage_material_combo = gtk_combo_new(); |
| |
188 glist = NULL; |
| |
189 glist = g_list_append(glist, "Red"); |
| |
190 glist = g_list_append(glist, "Dark Brown"); |
| |
191 glist = g_list_append(glist, "Light Brown"); |
| |
192 glist = g_list_append(glist, "White"); |
| |
193 glist = g_list_append(glist, "Green"); |
| |
194 glist = g_list_append(glist, "Black"); |
| |
195 gtk_combo_set_popdown_strings(GTK_COMBO(appendage_material_combo), |
| |
196 glist); |
| |
197 g_list_free(glist); |
| |
198 //gtk_combo_box_set_column_span_column(GTK_COMBO(appendage_material_combo), 10); |
| |
199 gtk_box_pack_start(GTK_BOX(hbox), appendage_material_combo, |
| |
200 TRUE, TRUE, 0); |
| |
201 gtk_widget_show(appendage_material_combo); |
| |
202 instance->appendage = appendage_material_combo; |
| |
203 |
| |
204 hbox = gtk_hbox_new(TRUE, 0); |
| |
205 gtk_box_pack_start(GTK_BOX(ret.vbox), hbox, FALSE, FALSE, 0); |
| |
206 gtk_widget_show(hbox); |
| |
207 |
| |
208 GtkWidget *lids_material_combo = gtk_combo_new(); |
| |
209 glist = NULL; |
| |
210 glist = g_list_append(glist, "Red"); |
| |
211 glist = g_list_append(glist, "Dark Brown"); |
| |
212 glist = g_list_append(glist, "Light Brown"); |
| |
213 glist = g_list_append(glist, "White"); |
| |
214 glist = g_list_append(glist, "Green"); |
| |
215 glist = g_list_append(glist, "Black"); |
| |
216 gtk_combo_set_popdown_strings(GTK_COMBO(lids_material_combo), glist); |
| |
217 g_list_free(glist); |
| |
218 //gtk_combo_box_set_column_span_column(GTK_COMBO(lids_material_combo), 10); |
| |
219 gtk_box_pack_start(GTK_BOX(hbox), lids_material_combo, TRUE, TRUE, 0); |
| |
220 gtk_widget_show(lids_material_combo); |
| |
221 instance->lid = lids_material_combo; |
| |
222 |
| |
223 GtkWidget *left_iris_material_combo = gtk_combo_new(); |
| |
224 glist = NULL; |
| |
225 glist = g_list_append(glist, "Red"); |
| |
226 glist = g_list_append(glist, "Dark Brown"); |
| |
227 glist = g_list_append(glist, "Light Brown"); |
| |
228 glist = g_list_append(glist, "White"); |
| |
229 glist = g_list_append(glist, "Green"); |
| |
230 glist = g_list_append(glist, "Black"); |
| |
231 gtk_combo_set_popdown_strings(GTK_COMBO(left_iris_material_combo), |
| |
232 glist); |
| |
233 g_list_free(glist); |
| |
234 //gtk_combo_box_set_column_span_column(GTK_COMBO(left_iris_material_combo), 10); |
| |
235 gtk_box_pack_start(GTK_BOX(hbox), left_iris_material_combo, |
| |
236 TRUE, TRUE, 0); |
| |
237 gtk_widget_show(left_iris_material_combo); |
| |
238 instance->left_iris = left_iris_material_combo; |
| |
239 |
| |
240 /* |
| |
241 GtkWidget *right_iris_material_combo = gtk_combo_new(); |
| |
242 glist = NULL; |
| |
243 glist = g_list_append(glist, "Red"); |
| |
244 glist = g_list_append(glist, "Dark Brown"); |
| |
245 glist = g_list_append(glist, "Light Brown"); |
| |
246 glist = g_list_append(glist, "White"); |
| |
247 glist = g_list_append(glist, "Green"); |
| |
248 glist = g_list_append(glist, "Black"); |
| |
249 gtk_combo_set_popdown_strings(GTK_COMBO(right_iris_material_combo), |
| |
250 glist); |
| |
251 g_list_free(glist); |
| |
252 //gtk_combo_box_set_column_span_column(GTK_COMBO(right_iris_material_combo), 10); |
| |
253 gtk_box_pack_start(GTK_BOX(hbox), right_iris_material_combo, |
| |
254 TRUE, TRUE, 0); |
| |
255 gtk_widget_show(right_iris_material_combo); |
| |
256 instance->right_iris = right_iris_material_combo; |
| |
257 */ |
| |
258 gtk_widget_add_events(ret.draw_area, GDK_BUTTON_PRESS_MASK); |
| |
259 g_signal_connect(G_OBJECT(ret.draw_area), "button_press_event", |
| |
260 G_CALLBACK(click_cb), instance); |
| |
261 g_signal_connect(G_OBJECT(ret.window), "destroy", |
| |
262 G_CALLBACK(destroy_cb), cc); |
| |
263 // gtk_widget_set_size_request(window, 640, 480); |
| |
264 gtk_window_set_default_size(GTK_WINDOW(ret.window),320,300); |
| |
265 |
| |
266 |
| |
267 GdkGeometry hints; |
| |
268 hints.max_width = 640; |
| |
269 hints.max_height = 480; |
| |
270 |
| |
271 gtk_window_set_geometry_hints (GTK_WINDOW(ret.window), |
| |
272 NULL, |
| |
273 &hints, |
| |
274 GDK_HINT_MAX_SIZE); |
| |
275 gtk_widget_show(ret.window); |
| |
276 return instance; |
| |
277 } |
| |
278 |
| |
279 static gboolean webcam_cb(struct input_instance *instance) |
| |
280 { |
| |
281 assert(instance); |
| |
282 QueryCam(); |
| |
283 return TRUE; |
| |
284 } |
| |
285 |
| |
286 static void *facefind(void *data) |
| |
287 { |
| |
288 fprintf(stderr, "waiting\n"); |
| |
289 getchar(); |
| |
290 fprintf(stderr,"got you"); |
| |
291 detection_mode=1; |
| |
292 return; |
| |
293 } |
| |
294 |
| |
295 void destroy_input(struct input_instance *instance) |
| |
296 { |
| |
297 extern filter_bank *bank; |
| |
298 assert(instance); |
| |
299 Filter_Destroy(bank); |
| |
300 g_source_remove(instance->timer_id); |
| |
301 Die(); |
| |
302 ExitMovies(); |
| |
303 } |
| |
304 |
| |
305 static gboolean config_wrapper(GtkWidget *widget, GdkEventConfigure *event, |
| |
306 void *data) |
| |
307 { |
| |
308 |
| |
309 |
| |
310 GdkGLContext *glcontext = gtk_widget_get_gl_context(widget); |
| |
311 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(widget); |
| |
312 |
| |
313 GLfloat w = widget->allocation.width; |
| |
314 GLfloat h = widget->allocation.height; |
| |
315 GLfloat aspect; |
| |
316 |
| |
317 // fprintf(stderr,"Homicide %f %f %d\n", w,h,draw_mode); |
| |
318 |
| |
319 if (draw_mode==1){ |
| |
320 // fprintf(stderr, "Bad place to be- actually not so bad\n"); |
| |
321 return configure(widget, event, data); |
| |
322 } |
| |
323 |
| |
324 /*** OpenGL BEGIN ***/ |
| |
325 if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext)) |
| |
326 return FALSE; |
| |
327 |
| |
328 |
| |
329 /* Undo all of the Model lighting here*/ |
| |
330 |
| |
331 // glDisable(GL_LIGHTING); |
| |
332 glDisable(GL_DEPTH_TEST); |
| |
333 // glDisable(GL_CULL_FACE); |
| |
334 // glDisable(GL_LIGHT0); |
| |
335 // glClearColor(1.0f, 1.0f, 1.0f, 1.0f); |
| |
336 /* */ |
| |
337 |
| |
338 |
| |
339 glViewport(0,-(h/14),w*2,h*2); |
| |
340 |
| |
341 glMatrixMode(GL_PROJECTION); |
| |
342 glLoadIdentity(); |
| |
343 gluOrtho2D(0,0,640,640); |
| |
344 glRasterPos2i(0,0); |
| |
345 glPixelZoom(-w/(1*640),(-h/(1*480))); |
| |
346 glMatrixMode(GL_MODELVIEW); |
| |
347 glLoadIdentity(); |
| |
348 |
| |
349 |
| |
350 |
| |
351 |
| |
352 gdk_gl_drawable_gl_end(gldrawable); |
| |
353 /*** OpenGL END ***/ |
| |
354 |
| |
355 return TRUE; |
| |
356 } |
| |
357 |
| |
358 static gboolean mydraw(GtkWidget *widget, GdkEventExpose *event, |
| |
359 void *data) |
| |
360 { |
| |
361 GdkGLContext *glcontext = gtk_widget_get_gl_context (widget); |
| |
362 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget); |
| |
363 struct input_instance *instance = (struct input_instance*)data; |
| |
364 unsigned int *boo; |
| |
365 struct cc_features *features = &instance->face; |
| |
366 |
| |
367 char *string = gtk_entry_get_text(GTK_COMBO(instance->model)->entry); |
| |
368 if (!strcmp(string, "Dog")) { |
| |
369 features->kind = 0; |
| |
370 } else if (!strcmp(string, "Shark")) { |
| |
371 features->kind = 1; |
| |
372 } |
| |
373 |
| |
374 material_set(GTK_ENTRY(GTK_COMBO(instance->head)->entry), |
| |
375 &features->head_color); |
| |
376 material_set(GTK_ENTRY(GTK_COMBO(instance->appendage)->entry), |
| |
377 &features->appendage_color); |
| |
378 material_set(GTK_ENTRY(GTK_COMBO(instance->lid)->entry), |
| |
379 &features->lid_color); |
| |
380 material_set(GTK_ENTRY(GTK_COMBO(instance->left_iris)->entry), |
| |
381 &features->left_iris_color); |
| |
382 material_set(GTK_ENTRY(GTK_COMBO(instance->left_iris)->entry), |
| |
383 &features->right_iris_color); |
| |
384 |
| |
385 if (easter_count>0) { |
| |
386 easter_count--; |
| |
387 } else { |
| |
388 instance->face.mode = 0; |
| |
389 } |
| |
390 |
| |
391 if (mode_change>0){ |
| |
392 mode_change--; |
| |
393 config_wrapper(widget, event, data); |
| |
394 } |
| |
395 |
| |
396 if (draw_mode==1){ |
| |
397 instance->output.my_output=LOCAL; |
| |
398 return draw(widget,event,&instance->output); |
| |
399 } |
| |
400 |
| |
401 |
| |
402 boo = (unsigned int*)colorBuf; |
| |
403 |
| |
404 assert(instance); |
| |
405 assert(gtk_widget_is_gl_capable(widget)); |
| |
406 |
| |
407 /*** OpenGL BEGIN ***/ |
| |
408 |
| |
409 if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext)) { |
| |
410 // fprintf(stderr, "We're fucked this time.\n"); |
| |
411 return FALSE; |
| |
412 } |
| |
413 |
| |
414 glClearColor(0.0, 0.0, 0.0, 0.0); |
| |
415 glClear(GL_COLOR_BUFFER_BIT); |
| |
416 |
| |
417 glDrawPixels(WEBCAM_X, WEBCAM_Y-70, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, boo); |
| |
418 |
| |
419 if (gdk_gl_drawable_is_double_buffered(gldrawable)) |
| |
420 gdk_gl_drawable_swap_buffers(gldrawable); |
| |
421 else |
| |
422 glFlush(); |
| |
423 |
| |
424 gdk_gl_drawable_gl_end(gldrawable); |
| |
425 |
| |
426 /*** OpenGL END ***/ |
| |
427 |
| |
428 return TRUE; |
| |
429 } |
| |
430 |
| |
431 static void init_cb(GtkWidget *widget, struct input_instance *instance) |
| |
432 { |
| |
433 // setupDrawlists(LOCAL); |
| |
434 // fprintf(stderr,"init_cb\n"); |
| |
435 } |
| |
436 |
| |
437 static gboolean click_cb(GtkWidget *widget, GdkEventButton *event, |
| |
438 struct input_instance *instance) |
| |
439 { |
| |
440 |
| |
441 GLfloat w = widget->allocation.width; |
| |
442 GLfloat h = widget->allocation.height; |
| |
443 GLfloat aspect; |
| |
444 |
| |
445 if (draw_mode==1) { |
| |
446 switch (event->button) { |
| |
447 case 1: |
| |
448 Debug("F U!\n"); |
| |
449 instance->face.mode = 1; |
| |
450 easter_count = 5; |
| |
451 break; |
| |
452 case 3: |
| |
453 Debug("should never get here\n"); |
| |
454 instance->face.mode = 2; |
| |
455 easter_count = 5; |
| |
456 break; |
| |
457 default: |
| |
458 instance->face.mode = 0; |
| |
459 break; |
| |
460 } |
| |
461 return FALSE; |
| |
462 } |
| |
463 |
| |
464 x_click=(event->x*(640/w)); |
| |
465 x_click=640-x_click; |
| |
466 y_click=(event->y-(h/14))*(480/(h-(h/14))); |
| |
467 detection_mode=1; |
| |
468 //Debug("@@@ x:%d y:%d\n", x_click, y_click); |
| |
469 |
| |
470 gtk_label_set_text(instance->label, |
| |
471 "Put on the box, then press confirm."); |
| |
472 if (x_click <= 10) x_click=10; |
| |
473 if (x_click >= WEBCAM_X-10) x_click=WEBCAM_X-60; |
| |
474 if (y_click <= 10) y_click=10; |
| |
475 if (y_click >= WEBCAM_Y-10) y_click=WEBCAM_Y-60; |
| |
476 |
| |
477 return FALSE; |
| |
478 } |
| |
479 |
| |
480 static void button_cb(GtkWidget *button, struct input_instance *instance) |
| |
481 { |
| |
482 if (!draw_mode) { /* transition to face mode */ |
| |
483 if (detection_mode == 0) { /* ignore confirm if no calibrate */ |
| |
484 return; |
| |
485 } |
| |
486 setupLighting(instance->widget); |
| |
487 mode_change = 1; |
| |
488 gtk_button_set_label(GTK_BUTTON(button), "Calibrate"); |
| |
489 gtk_label_set_label(instance->label, |
| |
490 "If things get too crazy, click Calibrate."); |
| |
491 } else { /* transition to calibration mode */ |
| |
492 gtk_label_set_label(instance->label, "Click your face"); |
| |
493 mode_change = 2; |
| |
494 gtk_button_set_label(GTK_BUTTON(button), "Confirm"); |
| |
495 } |
| |
496 draw_mode = !draw_mode; |
| |
497 } |
| |
498 |
| |
499 static void destroy_cb(GtkWidget *widget, struct crazychat *cc) |
| |
500 { |
| |
501 cc->features_state = 0; |
| |
502 destroy_input(cc->input_data); |
| |
503 cc->input_data = NULL; |
| |
504 } |
| |
505 |
| |
506 static void material_set(GtkWidget *entry, guint8 *material) |
| |
507 { |
| |
508 char *string = gtk_entry_get_text(GTK_ENTRY(entry)); |
| |
509 if (!strcmp(string, "Red")) { |
| |
510 *material = 0; |
| |
511 } else if (!strcmp(string, "Dark Brown")) { |
| |
512 *material = 1; |
| |
513 } else if (!strcmp(string, "Light Brown")) { |
| |
514 *material = 2; |
| |
515 } else if (!strcmp(string, "White")) { |
| |
516 *material = 3; |
| |
517 } else if (!strcmp(string, "Green")) { |
| |
518 *material = 4; |
| |
519 } else if (!strcmp(string, "Black")) { |
| |
520 *material = 5; |
| |
521 } |
| |
522 } |