00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "../../client.h"
00027 #include "../ui_main.h"
00028 #include "../ui_data.h"
00029 #include "../ui_windows.h"
00030 #include "../ui_nodes.h"
00031 #include "../ui_actions.h"
00032 #include "../ui_render.h"
00033 #include "../ui_parse.h"
00034 #include "ui_node_abstractnode.h"
00035 #include "ui_node_abstractscrollable.h"
00036 #include "../../cl_video.h"
00037 #include "../../renderer/r_image.h"
00038 #include "../../renderer/r_draw.h"
00039 #include "../../renderer/r_model.h"
00040 #include "ui_node_material_editor.h"
00041
00042 #define EXTRADATA(node) UI_EXTRADATA(node, abstractScrollableExtraData_t)
00043
00044
00046 #define IMAGE_WIDTH 64
00047
00048 typedef struct materialDescription_s {
00049 const char *name;
00050 const int stageFlag;
00051 } materialDescription_t;
00052
00053 static const materialDescription_t materialDescriptions[] = {
00054 {"texture", STAGE_TEXTURE},
00055 {"envmap", STAGE_ENVMAP},
00056 {"blend", STAGE_BLEND},
00057 {"color", STAGE_COLOR},
00058 {"pulse", STAGE_PULSE},
00059 {"stretch", STAGE_STRETCH},
00060 {"rotate", STAGE_ROTATE},
00061 {"scroll.s", STAGE_SCROLL_S},
00062 {"scroll.t", STAGE_SCROLL_T},
00063 {"scale.s", STAGE_SCALE_S},
00064 {"scale.t", STAGE_SCALE_T},
00065 {"terrain", STAGE_TERRAIN},
00066 {"tape", STAGE_TAPE},
00067 {"lightmap", STAGE_LIGHTMAP},
00068 {"anim", STAGE_ANIM},
00069 {"dirtmap", STAGE_DIRTMAP},
00070
00071 {NULL, 0}
00072 };
00073
00074 static materialStage_t *UI_MaterialEditorGetStage (material_t *material, int stageIndex)
00075 {
00076 materialStage_t *materialStage = material->stages;
00077 while (stageIndex-- > 0) {
00078 if (materialStage)
00079 materialStage = materialStage->next;
00080 else
00081 break;
00082 }
00083 return materialStage;
00084 }
00085
00089 static int UI_MaterialEditorNodeGetImageCount (uiNode_t *node)
00090 {
00091 int i;
00092 int cnt = 0;
00093
00094 for (i = 0; i < r_numImages; i++) {
00095 #ifndef ANYIMAGES
00096 const image_t *image = &r_images[i];
00097
00098 if (image->type != it_world)
00099 continue;
00100
00101 if (strstr(image->name, "tex_common"))
00102 continue;
00103 #endif
00104 cnt++;
00105 }
00106 return cnt;
00107 }
00108
00112 static void UI_MaterialEditorNodeUpdateView (uiNode_t *node, qboolean reset)
00113 {
00114 const int imageCount = UI_MaterialEditorNodeGetImageCount(node);
00115 const int imagesPerLine = (node->size[0] - node->padding) / (IMAGE_WIDTH + node->padding);
00116 const int imagesPerColumn = (node->size[1] - node->padding) / (IMAGE_WIDTH + node->padding);
00117
00118
00119 if (imagesPerLine > 0 && imagesPerColumn > 0) {
00120 const int pos = reset ? 0 : -1;
00121 UI_AbstractScrollableNodeSetY(node, pos, imagesPerColumn, imageCount / imagesPerLine);
00122 } else
00123 UI_AbstractScrollableNodeSetY(node, 0, 0, 0);
00124 }
00125
00129 static void UI_MaterialEditorNodeDraw (uiNode_t *node)
00130 {
00131 int i;
00132 vec2_t pos;
00133 int cnt = 0;
00134 int cntView = 0;
00135 const int imagesPerLine = (node->size[0] - node->padding) / (IMAGE_WIDTH + node->padding);
00136
00137 if (UI_AbstractScrollableNodeIsSizeChange(node))
00138 UI_MaterialEditorNodeUpdateView(node, qfalse);
00139
00140
00141 if (imagesPerLine <= 0)
00142 return;
00143
00144 UI_GetNodeAbsPos(node, pos);
00145
00146
00147 for (i = 0; i < r_numImages; i++) {
00148 image_t *image = &r_images[i];
00149 vec2_t imagepos;
00150
00151 #ifndef ANYIMAGES
00152
00153 if (image->type != it_world)
00154 continue;
00155
00156 if (strstr(image->name, "tex_common"))
00157 continue;
00158 #endif
00159
00160
00161 if (cnt / imagesPerLine < EXTRADATA(node).scrollY.viewPos) {
00162 cnt++;
00163 continue;
00164 }
00165
00167 imagepos[0] = pos[0] + node->padding + (cntView % imagesPerLine) * (IMAGE_WIDTH + node->padding);
00168 imagepos[1] = pos[1] + node->padding + (cntView / imagesPerLine) * (IMAGE_WIDTH + node->padding);
00169
00170
00171 if (imagepos[1] + IMAGE_WIDTH + node->padding >= pos[1] + node->size[1])
00172 break;
00173
00174 if (i == node->num) {
00175 #define MARGIN 3
00176 R_DrawRect(imagepos[0] - MARGIN, imagepos[1] - MARGIN, IMAGE_WIDTH + MARGIN * 2, IMAGE_WIDTH + MARGIN * 2, node->selectedColor, 2, 0xFFFF);
00177 #undef MARGIN
00178 }
00179
00180 UI_DrawNormImage(imagepos[0], imagepos[1], IMAGE_WIDTH, IMAGE_WIDTH, 0, 0, 0, 0, image);
00181
00182 cnt++;
00183 cntView++;
00184 }
00185 }
00186
00190 static int UI_MaterialEditorNodeGetImageAtPosition (uiNode_t *node, int x, int y)
00191 {
00192 int i;
00193 vec2_t pos;
00194 int cnt = 0;
00195 int cntView = 0;
00196 const int imagesPerLine = (node->size[0] - node->padding) / (IMAGE_WIDTH + node->padding);
00197 const int imagesPerColumn = (node->size[1] - node->padding) / (IMAGE_WIDTH + node->padding);
00198 int columnRequested;
00199 int lineRequested;
00200
00201 UI_NodeAbsoluteToRelativePos(node, &x, &y);
00202
00203
00204 if (((x % (IMAGE_WIDTH + node->padding)) < node->padding)
00205 || ((y % (IMAGE_WIDTH + node->padding)) < node->padding))
00206 return -1;
00207
00208
00209 columnRequested = x / (IMAGE_WIDTH + node->padding);
00210 lineRequested = y / (IMAGE_WIDTH + node->padding);
00211
00212
00213 if (columnRequested >= imagesPerLine || lineRequested >= imagesPerColumn)
00214 return -1;
00215
00216 UI_GetNodeAbsPos(node, pos);
00217
00218
00219 for (i = 0; i < r_numImages; i++) {
00220 #ifndef ANYIMAGES
00221
00222 image_t *image = &r_images[i];
00223 if (image->type != it_world)
00224 continue;
00225
00226 if (strstr(image->name, "tex_common"))
00227 continue;
00228 #endif
00229
00230
00231 if (cnt / imagesPerLine < EXTRADATA(node).scrollY.viewPos) {
00232 cnt++;
00233 continue;
00234 }
00235
00236 if (cntView % imagesPerLine == columnRequested && cntView / imagesPerLine == lineRequested)
00237 return i;
00238
00239
00240 if (cntView / imagesPerLine > lineRequested)
00241 break;
00242
00243 cnt++;
00244 cntView++;
00245 }
00246
00247 return -1;
00248 }
00249
00250 static void UI_MaterialEditorStagesToName (const materialStage_t *stage, char *buf, size_t size)
00251 {
00252 const materialDescription_t *md = materialDescriptions;
00253
00254 while (md->name) {
00255 if (stage->flags & md->stageFlag)
00256 Q_strcat(buf, va("%s ", md->name), size);
00257 md++;
00258 }
00259 }
00260
00266 static void UI_MaterialEditorUpdate (image_t *image, materialStage_t *materialStage)
00267 {
00268 linkedList_t *materialStagesList = NULL;
00269
00270 if (image->normalmap == NULL)
00271 UI_ExecuteConfunc("hideshaders true 0 0 0 0");
00272 else
00273 UI_ExecuteConfunc("hideshaders false %f %f %f %f", image->material.bump,
00274 image->material.hardness, image->material.parallax, image->material.specular);
00275
00276 if (image->normalmap == NULL)
00277 Cvar_Set("me_imagename", image->name);
00278 else
00279 Cvar_Set("me_imagename", va("%s (nm)", image->name));
00280
00281 if (!image->material.num_stages) {
00282 UI_ExecuteConfunc("hidestages true");
00283 } else {
00284 int i;
00285 if (materialStage) {
00286 const char *stageType = Cvar_GetString("me_stagetype");
00287 if (stageType[0] == '\0')
00288 stageType = "stretch";
00289 UI_ExecuteConfunc("hidestages false %s", stageType);
00290 } else
00291 Cvar_Set("me_stage_id", "-1");
00292 for (i = 0; i < image->material.num_stages; i++) {
00293 const materialStage_t *stage = UI_MaterialEditorGetStage(&image->material, i);
00294 char stageName[MAX_VAR] = "stage ";
00295 if (stage == materialStage) {
00296 UI_ExecuteConfunc("updatestagevalues %f %f %f %f %f %f %f %f %f %f %f %f %f %f",
00297 stage->rotate.hz, stage->rotate.deg,
00298 stage->stretch.hz, stage->stretch.dhz, stage->stretch.amp, stage->stretch.damp,
00299 stage->pulse.hz, stage->pulse.dhz,
00300 stage->scroll.ds, stage->scroll.dt, stage->scroll.s, stage->scroll.t,
00301 stage->scale.s, stage->scale.t);
00302 }
00303 UI_MaterialEditorStagesToName(stage, stageName, sizeof(stageName) - 1);
00304 LIST_AddString(&materialStagesList, stageName);
00305 }
00306 }
00307 UI_RegisterLinkedListText(TEXT_MATERIAL_STAGES, materialStagesList);
00308 }
00309
00315 static int UI_MaterialEditorNameToStage (const char *stageName)
00316 {
00317 const materialDescription_t *md = materialDescriptions;
00318
00319 while (md->name) {
00320 if (!strncmp(md->name, stageName, strlen(md->name)))
00321 return md->stageFlag;
00322 md++;
00323 }
00324 return -1;
00325 }
00326
00327 static void UI_MaterialEditorMouseDown (uiNode_t *node, int x, int y, int button)
00328 {
00329 int id;
00330 if (button != K_MOUSE1)
00331 return;
00332
00333 id = UI_MaterialEditorNodeGetImageAtPosition(node, x, y);
00334 if (id == -1)
00335 return;
00336
00338
00339 if (node->num != id) {
00340 image_t *image = &r_images[id];
00341 UI_MaterialEditorUpdate(image, NULL);
00342
00343 node->num = id;
00344
00345 if (node->onChange)
00346 UI_ExecuteEventActions(node, node->onChange);
00347 }
00348 }
00349
00353 static void UI_MaterialEditorNodeInit (uiNode_t *node)
00354 {
00355 node->num = -1;
00356 UI_MaterialEditorNodeUpdateView(node, qtrue);
00357 }
00358
00362 static void UI_MaterialEditorNodeWheel (uiNode_t *node, qboolean down, int x, int y)
00363 {
00364 const int diff = (down) ? 1 : -1;
00365 UI_AbstractScrollableNodeScrollY(node, diff);
00366 }
00367
00368 static void UI_MaterialEditorStart_f (void)
00369 {
00370
00371 #ifndef ANYIMAGES
00372 if (cls.state != ca_active) {
00373 Com_Printf("Material editor is only usable in battlescape mode\n");
00374 UI_PopWindow(qfalse);
00375 return;
00376 }
00377 #endif
00378 }
00379
00380 static const value_t materialValues[] = {
00381 {"bump", V_FLOAT, offsetof(material_t, bump), 0},
00382 {"parallax", V_FLOAT, offsetof(material_t, parallax), 0},
00383 {"specular", V_FLOAT, offsetof(material_t, specular), 0},
00384 {"hardness", V_FLOAT, offsetof(material_t, hardness), 0},
00385
00386 {NULL, V_NULL, 0, 0},
00387 };
00388
00389
00390 static const value_t materialStageValues[] = {
00391 {"rotate.hz", V_FLOAT, offsetof(materialStage_t, rotate.deg), 0},
00392 {"rotate.deg", V_FLOAT, offsetof(materialStage_t, rotate.hz), 0},
00393 {"stretch.hz", V_FLOAT, offsetof(materialStage_t, stretch.hz), 0},
00394 {"stretch.dhz", V_FLOAT, offsetof(materialStage_t, stretch.dhz), 0},
00395 {"stretch.amp", V_FLOAT, offsetof(materialStage_t, stretch.amp), 0},
00396 {"stretch.damp", V_FLOAT, offsetof(materialStage_t, stretch.damp), 0},
00397 {"pulse.hz", V_FLOAT, offsetof(materialStage_t, pulse.hz), 0},
00398 {"pulse.dhz", V_FLOAT, offsetof(materialStage_t, pulse.dhz), 0},
00399 {"scroll.s", V_FLOAT, offsetof(materialStage_t, scroll.s), 0},
00400 {"scroll.t", V_FLOAT, offsetof(materialStage_t, scroll.t), 0},
00401 {"scroll.ds", V_FLOAT, offsetof(materialStage_t, scroll.ds), 0},
00402 {"scroll.dt", V_FLOAT, offsetof(materialStage_t, scroll.dt), 0},
00403 {"scale.s", V_FLOAT, offsetof(materialStage_t, scale.s), 0},
00404 {"scale.t", V_FLOAT, offsetof(materialStage_t, scale.t), 0},
00405 {"terrain.floor", V_FLOAT, offsetof(materialStage_t, terrain.floor), 0},
00406 {"terrain.ceil", V_FLOAT, offsetof(materialStage_t, terrain.ceil), 0},
00407 {"tape.floor", V_FLOAT, offsetof(materialStage_t, tape.floor), 0},
00408 {"tape.ceil", V_FLOAT, offsetof(materialStage_t, tape.ceil), 0},
00409 {"tape.center", V_FLOAT, offsetof(materialStage_t, tape.center), 0},
00410 {"anim.frames", V_INT, offsetof(materialStage_t, anim.num_frames), 0},
00411 {"anim.dframe", V_INT, offsetof(materialStage_t, anim.dframe), 0},
00412 {"anim.dtime", V_FLOAT, offsetof(materialStage_t, anim.dtime), 0},
00413 {"anim.fps", V_FLOAT, offsetof(materialStage_t, anim.fps), 0},
00414 {"dirt.intensity", V_FLOAT, offsetof(materialStage_t, dirt.intensity), 0},
00415 {"blend.src", V_INT, offsetof(materialStage_t, blend.src), 0},
00416 {"blend.dest", V_INT, offsetof(materialStage_t, blend.dest), 0},
00417
00418 {NULL, V_NULL, 0, 0},
00419 };
00420
00421 static void UI_MaterialEditorChangeValue_f (void)
00422 {
00423 image_t *image;
00424 int id, stageType;
00425 const char *var, *value;
00426 size_t bytes;
00427
00428 if (Cmd_Argc() < 5) {
00429 Com_Printf("Usage: %s <image index> <stage index> <variable> <value>\n", Cmd_Argv(0));
00430 return;
00431 }
00432
00433 id = atoi(Cmd_Argv(1));
00434 if (id < 0 || id >= r_numImages) {
00435 Com_Printf("Given image index (%i) is out of bounds\n", id);
00436 return;
00437 }
00438
00439 var = Cmd_Argv(3);
00440 value = Cmd_Argv(4);
00441
00442 image = &r_images[id];
00443
00444 stageType = UI_MaterialEditorNameToStage(var);
00445 if (stageType == -1) {
00446 const value_t *val = UI_FindPropertyByName(materialValues, var);
00447 if (!val) {
00448 Com_Printf("Could not find material variable for '%s'\n", var);
00449 return;
00450 }
00451 Com_ParseValue(&image->material, value, val->type, val->ofs, val->size, &bytes);
00452 } else {
00453 materialStage_t *stage;
00454 int stageID;
00455 const value_t *val = UI_FindPropertyByName(materialStageValues, var);
00456
00457 if (!val) {
00458 Com_Printf("Could not find material stage variable for '%s'\n", var);
00459 return;
00460 }
00461
00462 stageID = atoi(Cmd_Argv(2));
00463 if (stageID < 0 || stageID >= image->material.num_stages) {
00464 Com_Printf("Given stage index (%i) is out of bounds\n", stageID);
00465 return;
00466 }
00467
00468 stage = UI_MaterialEditorGetStage(&image->material, stageID);
00469 assert(stage);
00470
00471 stage->flags |= stageType;
00472
00473 Com_ParseValue(stage, value, val->type, val->ofs, val->size, &bytes);
00474
00475
00476 if (stage->flags & (STAGE_TEXTURE | STAGE_ENVMAP))
00477 stage->flags |= STAGE_RENDER;
00478
00479 if (stage->flags & (STAGE_TAPE | STAGE_TERRAIN | STAGE_DIRTMAP))
00480 stage->flags |= STAGE_LIGHTING;
00481 }
00482
00483 R_ModReloadSurfacesArrays();
00484 }
00485
00486 static void UI_MaterialEditorSelectStage_f (void)
00487 {
00488 image_t *image;
00489 int id, stageID;
00490 materialStage_t *materialStage;
00491
00492 if (Cmd_Argc() < 3) {
00493 Com_Printf("Usage: %s <image index> <stage index>\n", Cmd_Argv(0));
00494 return;
00495 }
00496
00497 id = atoi(Cmd_Argv(1));
00498 if (id < 0 || id >= r_numImages) {
00499 Com_Printf("Given image index (%i) is out of bounds\n", id);
00500 return;
00501 }
00502
00503 image = &r_images[id];
00504
00505 stageID = atoi(Cmd_Argv(2));
00506 if (stageID < 0 || stageID >= image->material.num_stages) {
00507 Com_Printf("Given stage index (%i) is out of bounds\n", stageID);
00508 return;
00509 }
00510
00511 materialStage = UI_MaterialEditorGetStage(&image->material, stageID);
00512 UI_MaterialEditorUpdate(image, materialStage);
00513 }
00514
00515 static void UI_MaterialEditorRemoveStage_f (void)
00516 {
00517 image_t *image;
00518 int id, stageID;
00519
00520 if (Cmd_Argc() < 3) {
00521 Com_Printf("Usage: %s <image index> <stage index>\n", Cmd_Argv(0));
00522 return;
00523 }
00524
00525 id = atoi(Cmd_Argv(1));
00526 if (id < 0 || id >= r_numImages) {
00527 Com_Printf("Given image index (%i) is out of bounds\n", id);
00528 return;
00529 }
00530
00531 image = &r_images[id];
00532
00533 stageID = atoi(Cmd_Argv(2));
00534 if (stageID < 0 || stageID >= image->material.num_stages) {
00535 Com_Printf("Given stage index (%i) is out of bounds\n", stageID);
00536 return;
00537 }
00538
00539 if (stageID == 0) {
00540 materialStage_t *s = image->material.stages;
00541 image->material.stages = s->next;
00542 Mem_Free(s);
00543 } else {
00544 materialStage_t *sParent = UI_MaterialEditorGetStage(&image->material, stageID - 1);
00545 materialStage_t *s = sParent->next;
00546 sParent->next = s->next;
00547 Mem_Free(s);
00548 }
00549
00550 image->material.num_stages--;
00551
00552 UI_MaterialEditorUpdate(image, NULL);
00553 }
00554
00555 static void UI_MaterialEditorNewStage_f (void)
00556 {
00557 material_t *m;
00558 materialStage_t *s;
00559 int id;
00560
00561 if (Cmd_Argc() < 2) {
00562 Com_Printf("Usage: %s <image index>\n", Cmd_Argv(0));
00563 return;
00564 }
00565
00566 id = atoi(Cmd_Argv(1));
00567 if (id < 0 || id >= r_numImages) {
00568 Com_Printf("Given image index (%i) is out of bounds\n", id);
00569 return;
00570 }
00571
00572 m = &r_images[id].material;
00573 s = Mem_PoolAlloc(sizeof(*s), vid_imagePool, 0);
00574 m->num_stages++;
00575
00576
00577 if (!m->stages)
00578 m->stages = s;
00579 else {
00580 materialStage_t *ss = m->stages;
00581 while (ss->next)
00582 ss = ss->next;
00583 ss->next = s;
00584 }
00585
00586 UI_MaterialEditorUpdate(&r_images[id], s);
00587 }
00588
00589 void UI_RegisterMaterialEditorNode (uiBehaviour_t *behaviour)
00590 {
00591 behaviour->name = "material_editor";
00592 behaviour->extends = "abstractscrollable";
00593 behaviour->draw = UI_MaterialEditorNodeDraw;
00594 behaviour->init = UI_MaterialEditorNodeInit;
00595 behaviour->mouseDown = UI_MaterialEditorMouseDown;
00596 behaviour->mouseWheel = UI_MaterialEditorNodeWheel;
00597
00598 Cmd_AddCommand("mn_materialeditor_removestage", UI_MaterialEditorRemoveStage_f, "Removes the selected material stage");
00599 Cmd_AddCommand("mn_materialeditor_newstage", UI_MaterialEditorNewStage_f, "Creates a new material stage for the current selected material");
00600 Cmd_AddCommand("mn_materialeditor_selectstage", UI_MaterialEditorSelectStage_f, "Select a given material stage");
00601 Cmd_AddCommand("mn_materialeditor_changevalue", UI_MaterialEditorChangeValue_f, "Initializes the material editor window");
00602 Cmd_AddCommand("mn_materialeditor", UI_MaterialEditorStart_f, "Initializes the material editor window");
00603 }