Bug Summary

File:client/ui/node/ui_node_material_editor.cpp
Location:line 302, column 23
Description:Dereference of null pointer

Annotated Source Code

1/**
2 * @file
3 * @brief Material editor related code
4 */
5
6/*
7Copyright (C) 1997-2001 Id Software, Inc.
8
9This program is free software; you can redistribute it and/or
10modify it under the terms of the GNU General Public License
11as published by the Free Software Foundation; either version 2
12of the License, or (at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18See the GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24*/
25
26#include "../../client.h"
27#include "../ui_main.h"
28#include "../ui_data.h"
29#include "../ui_windows.h"
30#include "../ui_nodes.h"
31#include "../ui_behaviour.h"
32#include "../ui_actions.h"
33#include "../ui_render.h"
34#include "../ui_parse.h"
35#include "ui_node_abstractnode.h"
36#include "ui_node_abstractscrollable.h"
37#include "../../cl_video.h"
38#include "../../renderer/r_image.h"
39#include "../../renderer/r_draw.h"
40#include "../../renderer/r_model.h"
41#include "ui_node_material_editor.h"
42
43#define EXTRADATA(node)(*((abstractScrollableExtraData_t*)((char*)node + sizeof(uiNode_t
))))
UI_EXTRADATA(node, abstractScrollableExtraData_t)(*((abstractScrollableExtraData_t*)((char*)node + sizeof(uiNode_t
))))
44
45/*#define ANYIMAGES*/
46/** @todo Replace magic number 64 by some script definition */
47#define IMAGE_WIDTH64 64
48
49typedef struct materialDescription_s {
50 const char *name;
51 const int stageFlag;
52} materialDescription_t;
53
54static const materialDescription_t materialDescriptions[] = {
55 {"texture", STAGE_TEXTURE(1 << 0)},
56 {"envmap", STAGE_ENVMAP(1 << 1)},
57 {"blend", STAGE_BLEND(1 << 2)},
58 {"color", STAGE_COLOR(1 << 3)},
59 {"pulse", STAGE_PULSE(1 << 4)},
60 {"stretch", STAGE_STRETCH(1 << 5)},
61 {"rotate", STAGE_ROTATE(1 << 6)},
62 {"scroll.s", STAGE_SCROLL_S(1 << 7)},
63 {"scroll.t", STAGE_SCROLL_T(1 << 8)},
64 {"scale.s", STAGE_SCALE_S(1 << 9)},
65 {"scale.t", STAGE_SCALE_T(1 << 10)},
66 {"terrain", STAGE_TERRAIN(1 << 11)},
67 {"tape", STAGE_TAPE(1 << 12)},
68 {"lightmap", STAGE_LIGHTMAP(1 << 13)},
69 {"anim", STAGE_ANIM(1 << 14)},
70 {"dirtmap", STAGE_DIRTMAP(1 << 15)},
71
72 {NULL__null, 0}
73};
74
75static materialStage_t *UI_MaterialEditorGetStage (material_t *material, int stageIndex)
76{
77 materialStage_t *materialStage = material->stages;
78 while (stageIndex-- > 0) {
79 if (materialStage)
80 materialStage = materialStage->next;
81 else
82 break;
83 }
84 return materialStage;
85}
86
87/**
88 * @brief return the number of images we can display
89 */
90static int UI_MaterialEditorNodeGetImageCount (uiNode_t *node)
91{
92 int i;
93 int cnt = 0;
94
95 for (i = 0; i < r_numImages; i++) {
96#ifndef ANYIMAGES
97 const image_t *image = R_GetImageAtIndex(i);
98 /* filter */
99 if (image->type != it_world)
100 continue;
101
102 if (strstr(image->name, "tex_common"))
103 continue;
104#endif
105 cnt++;
106 }
107 return cnt;
108}
109
110/**
111 * @brief Update the scrollable view
112 */
113void uiMaterialEditorNode::updateView (uiNode_t *node, bool reset)
114{
115 const int imageCount = UI_MaterialEditorNodeGetImageCount(node);
116 const int imagesPerLine = (node->box.size[0] - node->padding) / (IMAGE_WIDTH64 + node->padding);
117 const int imagesPerColumn = (node->box.size[1] - node->padding) / (IMAGE_WIDTH64 + node->padding);
118
119 /* update view */
120 if (imagesPerLine > 0 && imagesPerColumn > 0) {
121 const int pos = reset ? 0 : -1;
122 setScrollY(node, pos, imagesPerColumn, imageCount / imagesPerLine);
123 } else
124 setScrollY(node, 0, 0, 0);
125}
126
127/**
128 * @param node The node to draw
129 */
130void uiMaterialEditorNode::draw (uiNode_t *node)
131{
132 int i;
133 vec2_t pos;
134 int cnt = 0;
135 int cntView = 0;
136 const int imagesPerLine = (node->box.size[0] - node->padding) / (IMAGE_WIDTH64 + node->padding);
137
138 if (isSizeChange(node))
139 updateView(node, false);
140
141 /* width too small to display anything */
142 if (imagesPerLine <= 0)
143 return;
144
145 UI_GetNodeAbsPos(node, pos);
146
147 /* display images */
148 for (i = 0; i < r_numImages; i++) {
149 image_t *image = R_GetImageAtIndex(i);
150 vec2_t imagepos;
151
152#ifndef ANYIMAGES
153 /* filter */
154 if (image->type != it_world)
155 continue;
156
157 if (strstr(image->name, "tex_common"))
158 continue;
159#endif
160
161 /* skip images before the scroll position */
162 if (cnt / imagesPerLine < EXTRADATA(node)(*((abstractScrollableExtraData_t*)((char*)node + sizeof(uiNode_t
))))
.scrollY.viewPos) {
163 cnt++;
164 continue;
165 }
166
167 /** @todo do it incremental. Don't need all this math */
168 imagepos[0] = pos[0] + node->padding + (cntView % imagesPerLine) * (IMAGE_WIDTH64 + node->padding);
169 imagepos[1] = pos[1] + node->padding + (cntView / imagesPerLine) * (IMAGE_WIDTH64 + node->padding);
170
171 /* vertical overflow */
172 if (imagepos[1] + IMAGE_WIDTH64 + node->padding >= pos[1] + node->box.size[1])
173 break;
174
175 if (i == node->num) {
176#define MARGIN 3
177 R_DrawRect(imagepos[0] - MARGIN, imagepos[1] - MARGIN, IMAGE_WIDTH64 + MARGIN * 2, IMAGE_WIDTH64 + MARGIN * 2, node->selectedColor, 2, 0xFFFF);
178#undef MARGIN
179 }
180
181 UI_DrawNormImage(false, imagepos[0], imagepos[1], IMAGE_WIDTH64, IMAGE_WIDTH64, 0, 0, 0, 0, image);
182
183 cnt++;
184 cntView++;
185 }
186}
187
188/**
189 * @brief Return index of the image (r_images[i]) else NULL
190 */
191static int UI_MaterialEditorNodeGetImageAtPosition (uiNode_t *node, int x, int y)
192{
193 int i;
194 vec2_t pos;
195 int cnt = 0;
196 int cntView = 0;
197 const int imagesPerLine = (node->box.size[0] - node->padding) / (IMAGE_WIDTH64 + node->padding);
198 const int imagesPerColumn = (node->box.size[1] - node->padding) / (IMAGE_WIDTH64 + node->padding);
199 int columnRequested;
200 int lineRequested;
201
202 UI_NodeAbsoluteToRelativePos(node, &x, &y);
203
204 /* have we click between 2 images? */
205 if (((x % (IMAGE_WIDTH64 + node->padding)) < node->padding)
206 || ((y % (IMAGE_WIDTH64 + node->padding)) < node->padding))
207 return -1;
208
209 /* get column and line of the image */
210 columnRequested = x / (IMAGE_WIDTH64 + node->padding);
211 lineRequested = y / (IMAGE_WIDTH64 + node->padding);
212
213 /* have we click outside? */
214 if (columnRequested >= imagesPerLine || lineRequested >= imagesPerColumn)
215 return -1;
216
217 UI_GetNodeAbsPos(node, pos);
218
219 /* check images */
220 for (i = 0; i < r_numImages; i++) {
221#ifndef ANYIMAGES
222 /* filter */
223 image_t *image = R_GetImageAtIndex(i);
224 if (image->type != it_world)
225 continue;
226
227 if (strstr(image->name, "tex_common"))
228 continue;
229#endif
230
231 /* skip images before the scroll position */
232 if (cnt / imagesPerLine < EXTRADATA(node)(*((abstractScrollableExtraData_t*)((char*)node + sizeof(uiNode_t
))))
.scrollY.viewPos) {
233 cnt++;
234 continue;
235 }
236
237 if (cntView % imagesPerLine == columnRequested && cntView / imagesPerLine == lineRequested)
238 return i;
239
240 /* vertical overflow */
241 if (cntView / imagesPerLine > lineRequested)
242 break;
243
244 cnt++;
245 cntView++;
246 }
247
248 return -1;
249}
250
251static void UI_MaterialEditorStagesToName (const materialStage_t *stage, char *buf, size_t size)
252{
253 const materialDescription_t *md = materialDescriptions;
254
255 while (md->name) {
256 if (stage->flags & md->stageFlag)
257 Q_strcat(buf, va("%s ", md->name), size);
258 md++;
259 }
260}
261
262/**
263 * Updates the material editor node for a given image and a given material stage
264 * @param image The image to load into the material editor
265 * @param materialStage The material stage to display
266 */
267static void UI_MaterialEditorUpdate (image_t *image, materialStage_t *materialStage)
268{
269 linkedList_t *materialStagesList = NULL__null;
270
271 if (image->normalmap == NULL__null)
5
Taking true branch
272 UI_ExecuteConfunc("hideshaders true 0 0 0 0");
273 else
274 UI_ExecuteConfunc("hideshaders false %f %f %f %f", image->material.bump,
275 image->material.hardness, image->material.parallax, image->material.specular);
276
277 if (image->normalmap == NULL__null)
6
Taking true branch
278 Cvar_Set("me_imagename", image->name);
279 else
280 Cvar_Set("me_imagename", va("%s (nm)", image->name));
281
282 if (!image->material.num_stages) {
7
Taking false branch
283 UI_ExecuteConfunc("hidestages true");
284 } else {
285 int i;
286 if (materialStage) {
8
Taking false branch
287 const char *stageType = Cvar_GetString("me_stagetype");
288 if (stageType[0] == '\0')
289 stageType = "stretch";
290 UI_ExecuteConfunc("hidestages false %s", stageType);
291 } else
292 Cvar_Set("me_stage_id", "-1");
293 for (i = 0; i < image->material.num_stages; i++) {
9
Loop condition is true. Entering loop body
294 const materialStage_t *stage = UI_MaterialEditorGetStage(&image->material, i);
295 char stageName[MAX_VAR64] = "stage ";
296 if (stage == materialStage) {
10
Taking true branch
297 UI_ExecuteConfunc("updatestagevalues %f %f %f %f %f %f %f %f %f %f %f %f %f %f",
298 stage->rotate.hz, stage->rotate.deg,
299 stage->stretch.hz, stage->stretch.dhz, stage->stretch.amp, stage->stretch.damp,
300 stage->pulse.hz, stage->pulse.dhz,
301 stage->scroll.ds, stage->scroll.dt, stage->scroll.s, stage->scroll.t,
302 stage->scale.s, stage->scale.t);
11
Dereference of null pointer
303 }
304 UI_MaterialEditorStagesToName(stage, stageName, sizeof(stageName) - 1);
305 LIST_AddString(&materialStagesList, stageName);
306 }
307 }
308 UI_RegisterLinkedListText(TEXT_MATERIAL_STAGES, materialStagesList);
309}
310
311/**
312 * Converts a stage name into the stage flag
313 * @param stageName The name to search the flag for
314 * @return -1 if no flag was not found for the given name
315 */
316static int UI_MaterialEditorNameToStage (const char *stageName)
317{
318 const materialDescription_t *md = materialDescriptions;
319
320 while (md->name) {
321 if (!strncmp(md->name, stageName, strlen(md->name)))
322 return md->stageFlag;
323 md++;
324 }
325 return -1;
326}
327
328void uiMaterialEditorNode::onMouseDown (uiNode_t *node, int x, int y, int button)
329{
330 int id;
331 if (button != K_MOUSE1)
1
Taking false branch
332 return;
333
334 id = UI_MaterialEditorNodeGetImageAtPosition(node, x, y);
335 if (id == -1)
2
Taking false branch
336 return;
337
338 /** @note here we use "num" to cache the selected image id. We can reuse it on the script with "<num>" */
339 /* have we selected a new image? */
340 if (node->num != id) {
3
Taking true branch
341 image_t *image = R_GetImageAtIndex(id);
342 UI_MaterialEditorUpdate(image, NULL__null);
4
Calling 'UI_MaterialEditorUpdate'
343
344 node->num = id;
345
346 if (node->onChange)
347 UI_ExecuteEventActions(node, node->onChange);
348 }
349}
350
351/**
352 * @brief Called when we push a window with this node
353 */
354void uiMaterialEditorNode::onWindowOpened (uiNode_t *node, linkedList_t *params)
355{
356 node->num = -1;
357 updateView(node, true);
358}
359
360/**
361 * @brief Called when the user wheel the mouse over the node
362 */
363bool uiMaterialEditorNode::onScroll (uiNode_t *node, int deltaX, int deltaY)
364{
365 bool down = deltaY > 0;
366 const int diff = (down) ? 1 : -1;
367 if (deltaY == 0)
368 return false;
369 return scrollY(node, diff);
370}
371
372static void UI_MaterialEditorStart_f (void)
373{
374 /* material editor only makes sense in battlescape mode */
375#ifndef ANYIMAGES
376 if (cls.state != ca_active) {
377 Com_Printf("Material editor is only usable in battlescape mode\n");
378 UI_PopWindow(false);
379 return;
380 }
381#endif
382}
383
384static const value_t materialValues[] = {
385 {"bump", V_FLOAT, offsetof(material_t, bump)__builtin_offsetof(material_t, bump), 0},
386 {"parallax", V_FLOAT, offsetof(material_t, parallax)__builtin_offsetof(material_t, parallax), 0},
387 {"specular", V_FLOAT, offsetof(material_t, specular)__builtin_offsetof(material_t, specular), 0},
388 {"hardness", V_FLOAT, offsetof(material_t, hardness)__builtin_offsetof(material_t, hardness), 0},
389
390 {NULL__null, V_NULL, 0, 0},
391};
392
393
394static const value_t materialStageValues[] = {
395 {"rotate.hz", V_FLOAT, offsetof(materialStage_t, rotate.deg)__builtin_offsetof(materialStage_t, rotate.deg), 0},
396 {"rotate.deg", V_FLOAT, offsetof(materialStage_t, rotate.hz)__builtin_offsetof(materialStage_t, rotate.hz), 0},
397 {"stretch.hz", V_FLOAT, offsetof(materialStage_t, stretch.hz)__builtin_offsetof(materialStage_t, stretch.hz), 0},
398 {"stretch.dhz", V_FLOAT, offsetof(materialStage_t, stretch.dhz)__builtin_offsetof(materialStage_t, stretch.dhz), 0},
399 {"stretch.amp", V_FLOAT, offsetof(materialStage_t, stretch.amp)__builtin_offsetof(materialStage_t, stretch.amp), 0},
400 {"stretch.damp", V_FLOAT, offsetof(materialStage_t, stretch.damp)__builtin_offsetof(materialStage_t, stretch.damp), 0},
401 {"pulse.hz", V_FLOAT, offsetof(materialStage_t, pulse.hz)__builtin_offsetof(materialStage_t, pulse.hz), 0},
402 {"pulse.dhz", V_FLOAT, offsetof(materialStage_t, pulse.dhz)__builtin_offsetof(materialStage_t, pulse.dhz), 0},
403 {"scroll.s", V_FLOAT, offsetof(materialStage_t, scroll.s)__builtin_offsetof(materialStage_t, scroll.s), 0},
404 {"scroll.t", V_FLOAT, offsetof(materialStage_t, scroll.t)__builtin_offsetof(materialStage_t, scroll.t), 0},
405 {"scroll.ds", V_FLOAT, offsetof(materialStage_t, scroll.ds)__builtin_offsetof(materialStage_t, scroll.ds), 0},
406 {"scroll.dt", V_FLOAT, offsetof(materialStage_t, scroll.dt)__builtin_offsetof(materialStage_t, scroll.dt), 0},
407 {"scale.s", V_FLOAT, offsetof(materialStage_t, scale.s)__builtin_offsetof(materialStage_t, scale.s), 0},
408 {"scale.t", V_FLOAT, offsetof(materialStage_t, scale.t)__builtin_offsetof(materialStage_t, scale.t), 0},
409 {"terrain.floor", V_FLOAT, offsetof(materialStage_t, terrain.floor)__builtin_offsetof(materialStage_t, terrain.floor), 0},
410 {"terrain.ceil", V_FLOAT, offsetof(materialStage_t, terrain.ceil)__builtin_offsetof(materialStage_t, terrain.ceil), 0},
411 {"tape.floor", V_FLOAT, offsetof(materialStage_t, tape.floor)__builtin_offsetof(materialStage_t, tape.floor), 0},
412 {"tape.ceil", V_FLOAT, offsetof(materialStage_t, tape.ceil)__builtin_offsetof(materialStage_t, tape.ceil), 0},
413 {"tape.center", V_FLOAT, offsetof(materialStage_t, tape.center)__builtin_offsetof(materialStage_t, tape.center), 0},
414 {"anim.frames", V_INT, offsetof(materialStage_t, anim.num_frames)__builtin_offsetof(materialStage_t, anim.num_frames), 0},
415 {"anim.dframe", V_INT, offsetof(materialStage_t, anim.dframe)__builtin_offsetof(materialStage_t, anim.dframe), 0},
416 {"anim.dtime", V_FLOAT, offsetof(materialStage_t, anim.dtime)__builtin_offsetof(materialStage_t, anim.dtime), 0},
417 {"anim.fps", V_FLOAT, offsetof(materialStage_t, anim.fps)__builtin_offsetof(materialStage_t, anim.fps), 0},
418 {"dirt.intensity", V_FLOAT, offsetof(materialStage_t, dirt.intensity)__builtin_offsetof(materialStage_t, dirt.intensity), 0},
419 {"blend.src", V_INT, offsetof(materialStage_t, blend.src)__builtin_offsetof(materialStage_t, blend.src), 0},
420 {"blend.dest", V_INT, offsetof(materialStage_t, blend.dest)__builtin_offsetof(materialStage_t, blend.dest), 0},
421
422 {NULL__null, V_NULL, 0, 0},
423};
424
425static void UI_MaterialEditorChangeValue_f (void)
426{
427 image_t *image;
428 int id, stageType;
429 const char *var, *value;
430 size_t bytes;
431
432 if (Cmd_Argc() < 5) {
433 Com_Printf("Usage: %s <image index> <stage index> <variable> <value>\n", Cmd_Argv(0));
434 return;
435 }
436
437 id = atoi(Cmd_Argv(1));
438 if (id < 0 || id >= r_numImages) {
439 Com_Printf("Given image index (%i) is out of bounds\n", id);
440 return;
441 }
442
443 var = Cmd_Argv(3);
444 value = Cmd_Argv(4);
445
446 image = R_GetImageAtIndex(id);
447
448 stageType = UI_MaterialEditorNameToStage(var);
449 if (stageType == -1) {
450 const value_t *val = UI_FindPropertyByName(materialValues, var);
451 if (!val) {
452 Com_Printf("Could not find material variable for '%s'\n", var);
453 return;
454 }
455 Com_ParseValue(&image->material, value, val->type, val->ofs, val->size, &bytes);
456 } else {
457 materialStage_t *stage;
458 int stageID;
459 const value_t *val = UI_FindPropertyByName(materialStageValues, var);
460
461 if (!val) {
462 Com_Printf("Could not find material stage variable for '%s'\n", var);
463 return;
464 }
465
466 stageID = atoi(Cmd_Argv(2));
467 if (stageID < 0 || stageID >= image->material.num_stages) {
468 Com_Printf("Given stage index (%i) is out of bounds\n", stageID);
469 return;
470 }
471
472 stage = UI_MaterialEditorGetStage(&image->material, stageID);
473 assert(stage)(__builtin_expect(!(stage), 0) ? __assert_rtn(__func__, "src/client/ui/node/ui_node_material_editor.cpp"
, 473, "stage") : (void)0)
;
474
475 stage->flags |= stageType;
476
477 Com_ParseValue(stage, value, val->type, val->ofs, val->size, &bytes);
478
479 /* a texture or envmap means render it */
480 if (stage->flags & (STAGE_TEXTURE(1 << 0) | STAGE_ENVMAP(1 << 1)))
481 stage->flags |= STAGE_RENDER(1 << 31);
482
483 if (stage->flags & (STAGE_TAPE(1 << 12) | STAGE_TERRAIN(1 << 11) | STAGE_DIRTMAP(1 << 15)))
484 stage->flags |= STAGE_LIGHTING(1 << 30);
485 }
486
487 R_ModReloadSurfacesArrays();
488}
489
490static void UI_MaterialEditorSelectStage_f (void)
491{
492 image_t *image;
493 int id, stageID;
494 materialStage_t *materialStage;
495
496 if (Cmd_Argc() < 3) {
497 Com_Printf("Usage: %s <image index> <stage index>\n", Cmd_Argv(0));
498 return;
499 }
500
501 id = atoi(Cmd_Argv(1));
502 if (id < 0 || id >= r_numImages) {
503 Com_Printf("Given image index (%i) is out of bounds\n", id);
504 return;
505 }
506
507 image = R_GetImageAtIndex(id);
508
509 stageID = atoi(Cmd_Argv(2));
510 if (stageID < 0 || stageID >= image->material.num_stages) {
511 Com_Printf("Given stage index (%i) is out of bounds\n", stageID);
512 return;
513 }
514
515 materialStage = UI_MaterialEditorGetStage(&image->material, stageID);
516 UI_MaterialEditorUpdate(image, materialStage);
517}
518
519static void UI_MaterialEditorRemoveStage_f (void)
520{
521 image_t *image;
522 int id, stageID;
523
524 if (Cmd_Argc() < 3) {
525 Com_Printf("Usage: %s <image index> <stage index>\n", Cmd_Argv(0));
526 return;
527 }
528
529 id = atoi(Cmd_Argv(1));
530 if (id < 0 || id >= r_numImages) {
531 Com_Printf("Given image index (%i) is out of bounds\n", id);
532 return;
533 }
534
535 image = R_GetImageAtIndex(id);
536
537 stageID = atoi(Cmd_Argv(2));
538 if (stageID < 0 || stageID >= image->material.num_stages) {
539 Com_Printf("Given stage index (%i) is out of bounds\n", stageID);
540 return;
541 }
542
543 materialStage_t** const anchor = stageID == 0 ? &image->material.stages : &UI_MaterialEditorGetStage(&image->material, stageID - 1)->next;
544 materialStage_t* const s = *anchor;
545 *anchor = s->next;
546 Mem_Free(s)_Mem_Free((s),"src/client/ui/node/ui_node_material_editor.cpp"
,546)
;
547
548 image->material.num_stages--;
549
550 UI_MaterialEditorUpdate(image, NULL__null);
551}
552
553static void UI_MaterialEditorNewStage_f (void)
554{
555 material_t *m;
556 int id;
557
558 if (Cmd_Argc() < 2) {
559 Com_Printf("Usage: %s <image index>\n", Cmd_Argv(0));
560 return;
561 }
562
563 id = atoi(Cmd_Argv(1));
564 if (id < 0 || id >= r_numImages) {
565 Com_Printf("Given image index (%i) is out of bounds\n", id);
566 return;
567 }
568
569 m = &R_GetImageAtIndex(id)->material;
570 materialStage_t* const s = Mem_PoolAllocType(materialStage_t, vid_imagePool)static_cast<materialStage_t*>(static_cast<materialStage_t
*>(_Mem_Alloc((sizeof(materialStage_t) * (1)),true,(((vid_imagePool
))),(0),"src/client/ui/node/ui_node_material_editor.cpp",570)
))
;
571 m->num_stages++;
572
573 /* append the stage to the chain */
574 if (!m->stages)
575 m->stages = s;
576 else {
577 materialStage_t *ss = m->stages;
578 while (ss->next)
579 ss = ss->next;
580 ss->next = s;
581 }
582
583 UI_MaterialEditorUpdate(R_GetImageAtIndex(id), s);
584}
585
586void UI_RegisterMaterialEditorNode (uiBehaviour_t *behaviour)
587{
588 behaviour->name = "material_editor";
589 behaviour->extends = "abstractscrollable";
590 behaviour->manager = new uiMaterialEditorNode();
591
592 /** @todo convert it to ui functions */
593 Cmd_AddCommand("ui_materialeditor_removestage", UI_MaterialEditorRemoveStage_f, "Removes the selected material stage");
594 Cmd_AddCommand("ui_materialeditor_newstage", UI_MaterialEditorNewStage_f, "Creates a new material stage for the current selected material");
595 Cmd_AddCommand("ui_materialeditor_selectstage", UI_MaterialEditorSelectStage_f, "Select a given material stage");
596 Cmd_AddCommand("ui_materialeditor_changevalue", UI_MaterialEditorChangeValue_f, "Initializes the material editor window");
597 Cmd_AddCommand("ui_materialeditor", UI_MaterialEditorStart_f, "Initializes the material editor window");
598}