]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/tools/materialeditor/MaterialDoc.cpp
hello world
[icculus/iodoom3.git] / neo / tools / materialeditor / MaterialDoc.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28 #include "../../idlib/precompiled.h"
29 #pragma hdrstop
30
31 #include "MaterialDoc.h"
32 #include "MaterialView.h"
33
34 /**
35 * Constructor for MaterialDoc.
36 */
37 MaterialDoc::MaterialDoc(void) {
38         modified = false;
39         applyWaiting = false;
40         sourceModify = false;
41 }
42
43 /**
44 * Destructor for MaterialDoc.
45 */
46 MaterialDoc::~MaterialDoc(void) {
47         ClearEditMaterial();
48 }
49
50 /**
51 * Initializes the MaterialDoc instance with a specific idMaterial. This method will 
52 * parse the material into the internal dictionary representation and optionally 
53 * allow the idMaterial object to reparse the source.
54 * @param material The idMaterial instance to use.
55 * @param parseMaterial Flag to determine if the material should be parsed into the editor representation.
56 * @param parseRenderMaterial Flag to determine if the idMaterial object should be reparsed.
57 */
58 void MaterialDoc::SetRenderMaterial(idMaterial* material, bool parseMaterial, bool parseRenderMatierial) {
59
60         renderMaterial = material;
61
62
63         if(!parseMaterial ||  !renderMaterial)  
64                 return;
65
66         if(parseRenderMatierial) {
67                 char *declText = (char *) _alloca( material->GetTextLength() + 1 );
68                 material->GetText( declText );
69
70                 renderMaterial->GetText(declText);
71                 ParseMaterialText(declText);
72
73         }
74
75         ClearEditMaterial();
76
77         name = material->GetName();
78
79         idLexer         src;
80
81         char *declText = (char *) _alloca( material->GetTextLength() + 1 );
82         material->GetText( declText );
83
84         renderMaterial->GetText(declText);
85         src.LoadMemory(declText, strlen(declText), "Material");
86
87         ParseMaterial(&src);
88 }
89
90 /**
91 * Returns the number of stages in this material.
92 */
93 int     MaterialDoc::GetStageCount() {
94         return editMaterial.stages.Num();
95 }
96
97 /**
98 * Returns the index of the stage with the specified type and name or -1
99 * if the stage does not exist.
100 * @param stageType The type of stage to find.
101 * @param name The name of the stage to find.
102 */
103 int     MaterialDoc::FindStage(int stageType, const char* name) {
104         
105         for(int i = 0; i < editMaterial.stages.Num(); i++) {
106                 int type = GetAttributeInt(i, "stagetype");
107                 idStr localname = GetAttribute(i, "name");
108                 if(stageType == type && !localname.Icmp(name))
109                         return i;
110         }
111         return -1;
112 }
113
114 /**
115 * Returns a copy of the specified stage.
116 * @param stage The stage to return.
117 */
118 MEStage_t MaterialDoc::GetStage(int stage) {
119         assert(stage >= 0 && stage < GetStageCount());
120         return *editMaterial.stages[stage];
121
122 }
123
124 /**
125 * Specifies the enabled state of a single stage.
126 * @param stage The stage to change.
127 * @param enabled The enabled state.
128 */
129 void MaterialDoc::EnableStage(int stage, bool enabled) {
130
131         assert(stage >= 0 && stage < GetStageCount());
132         editMaterial.stages[stage]->enabled = enabled;
133
134         OnMaterialChanged();
135 }
136
137 /**
138 * Sets the enabled state of all stages.
139 * @param enabled The enabled state.
140 */
141 void MaterialDoc::EnableAllStages(bool enabled) {
142         for(int i = 0; i < GetStageCount(); i++) {
143                 editMaterial.stages[i]->enabled = enabled;
144         }
145 }
146
147 /**
148 * Returns the enabled state of a stage.
149 * @param stage The stage to check.
150 */
151 bool MaterialDoc::IsStageEnabled(int stage) {
152         assert(stage >= 0 && stage < GetStageCount());
153         return editMaterial.stages[stage]->enabled;
154 }
155
156 /**
157 * Returns an attribute string from the material or a stage.
158 * @param stage The stage or -1 for the material.
159 * @param attribName The name of the attribute.
160 * @param defaultString The default value if the attribute is not specified.
161 */
162 const char*     MaterialDoc::GetAttribute(int stage, const char* attribName, const char* defaultString) {
163
164         if(stage == -1) {
165                 return editMaterial.materialData.GetString(attribName, defaultString);
166         } else {
167                 assert(stage >= 0 && stage < GetStageCount());
168                 MEStage_t* pStage = editMaterial.stages[stage];
169                 return pStage->stageData.GetString(attribName, defaultString);
170         }
171 }
172
173 /**
174 * Returns an attribute int from the material or a stage.
175 * @param stage The stage or -1 for the material.
176 * @param attribName The name of the attribute.
177 * @param defaultString The default value if the attribute is not specified.
178 */
179 int MaterialDoc::GetAttributeInt(int stage, const char* attribName, const char* defaultString) {
180         if(stage == -1) {
181                 return editMaterial.materialData.GetInt(attribName, defaultString);
182         } else {
183                 assert(stage >= 0 && stage < GetStageCount());
184                 MEStage_t* pStage = editMaterial.stages[stage];
185                 return pStage->stageData.GetInt(attribName, defaultString);
186         }
187 }
188
189 /**
190 * Returns an attribute float from the material or a stage.
191 * @param stage The stage or -1 for the material.
192 * @param attribName The name of the attribute.
193 * @param defaultString The default value if the attribute is not specified.
194 */
195 float MaterialDoc::GetAttributeFloat(int stage, const char* attribName, const char* defaultString) {
196         if(stage == -1) {
197                 return editMaterial.materialData.GetFloat(attribName, defaultString);
198         } else {
199                 assert(stage >= 0 && stage < GetStageCount());
200                 MEStage_t* pStage = editMaterial.stages[stage];
201                 return pStage->stageData.GetFloat(attribName, defaultString);
202         }
203 }
204
205 /**
206 * Returns an attribute bool from the material or a stage.
207 * @param stage The stage or -1 for the material.
208 * @param attribName The name of the attribute.
209 * @param defaultString The default value if the attribute is not specified.
210 */
211 bool MaterialDoc::GetAttributeBool(int stage, const char* attribName, const char* defaultString) {
212         if(stage == -1) {
213                 return editMaterial.materialData.GetBool(attribName, defaultString);
214         } else {
215                 assert(stage >= 0 && stage < GetStageCount());
216                 MEStage_t* pStage = editMaterial.stages[stage];
217                 return pStage->stageData.GetBool(attribName, defaultString);
218         }
219 }
220
221 /**
222 * Sets an attribute string in the material or a stage.
223 * @param stage The stage or -1 for the material.
224 * @param attribName The name of the attribute.
225 * @param value The value to set.
226 * @param addUndo Flag that specifies if the system should add an undo operation.
227 */
228 void MaterialDoc::SetAttribute(int stage, const char* attribName, const char* value, bool addUndo) {
229         
230         //Make sure we need to set the attribute
231         idStr orig  = GetAttribute(stage, attribName);
232         if(orig.Icmp(value)) {
233                 
234                 idDict* dict;
235                 if(stage == -1) {
236                         dict = &editMaterial.materialData;
237                 } else {
238                         assert(stage >= 0 && stage < GetStageCount());
239                         dict = &editMaterial.stages[stage]->stageData;
240                 }
241
242                 if(addUndo) {
243                         //Create a new Modifier for this change so we can undo and redo later
244                         AttributeMaterialModifierString* mod = new AttributeMaterialModifierString(manager, name, stage, attribName, value, orig);
245                         manager->AddMaterialUndoModifier(mod);
246                 }
247
248                 dict->Set(attribName, value);
249                 
250                 manager->AttributeChanged(this, stage, attribName);
251                 OnMaterialChanged();
252         }
253 }
254
255 /**
256 * Sets an attribute int in the material or a stage.
257 * @param stage The stage or -1 for the material.
258 * @param attribName The name of the attribute.
259 * @param value The value to set.
260 * @param addUndo Flag that specifies if the system should add an undo operation.
261 */
262 void MaterialDoc::SetAttributeInt(int stage, const char* attribName, int value, bool addUndo) {
263         //Make sure we need to set the attribute
264         int orig  = GetAttributeInt(stage, attribName);
265         if(orig != value) {
266
267                 idDict* dict;
268                 if(stage == -1) {
269                         dict = &editMaterial.materialData;
270                 } else {
271                         assert(stage >= 0 && stage < GetStageCount());
272                         dict = &editMaterial.stages[stage]->stageData;
273                 }
274
275                 dict->SetInt(attribName, value);
276
277                 manager->AttributeChanged(this, stage, attribName);
278                 OnMaterialChanged();
279         }
280 }
281
282 /**
283 * Sets an attribute float in the material or a stage.
284 * @param stage The stage or -1 for the material.
285 * @param attribName The name of the attribute.
286 * @param value The value to set.
287 * @param addUndo Flag that specifies if the system should add an undo operation.
288 */
289 void MaterialDoc::SetAttributeFloat(int stage, const char* attribName, float value, bool addUndo) {
290         //Make sure we need to set the attribute
291         float orig  = GetAttributeFloat(stage, attribName);
292         if(orig != value) {
293
294                 idDict* dict;
295                 if(stage == -1) {
296                         dict = &editMaterial.materialData;
297                 } else {
298                         assert(stage >= 0 && stage < GetStageCount());
299                         dict = &editMaterial.stages[stage]->stageData;
300                 }
301
302                 dict->SetFloat(attribName, value);
303
304                 manager->AttributeChanged(this, stage, attribName);
305                 OnMaterialChanged();
306         }
307 }
308
309 /**
310 * Sets an attribute bool in the material or a stage.
311 * @param stage The stage or -1 for the material.
312 * @param attribName The name of the attribute.
313 * @param value The value to set.
314 * @param addUndo Flag that specifies if the system should add an undo operation.
315 */
316 void MaterialDoc::SetAttributeBool(int stage, const char* attribName, bool value, bool addUndo) {
317         //Make sure we need to set the attribute
318         bool orig  = GetAttributeBool(stage, attribName);
319         if(orig != value) {
320
321                 idDict* dict;
322                 if(stage == -1) {
323                         dict = &editMaterial.materialData;
324                 } else {
325                         assert(stage >= 0 && stage < GetStageCount());
326                         dict = &editMaterial.stages[stage]->stageData;
327                 }
328
329                 if(addUndo) {
330                         //Create a new Modifier for this change so we can undo and redo later
331                         AttributeMaterialModifierBool* mod = new AttributeMaterialModifierBool(manager, name, stage, attribName, value, orig);
332                         manager->AddMaterialUndoModifier(mod);
333                 }
334
335                 dict->SetBool(attribName, value);
336
337                 manager->AttributeChanged(this, stage, attribName);
338                 OnMaterialChanged();
339         }
340 }
341
342 /**
343 * Sets the material name.
344 * @param materialName The new name of the material.
345 * @param addUndo Flag that specifies if the system should add an undo operation.
346 */
347 void MaterialDoc::SetMaterialName(const char* materialName, bool addUndo) {
348         idStr oldName = name;
349
350         declManager->RenameDecl(DECL_MATERIAL, oldName, materialName); 
351         name = renderMaterial->GetName();
352         
353         if(addUndo) {
354                 RenameMaterialModifier* mod = new RenameMaterialModifier(manager, name, oldName);
355                 manager->AddMaterialUndoModifier(mod);
356         }
357
358         manager->MaterialNameChanged(oldName, this);
359
360         OnMaterialChanged();
361
362         //Need to do an instant apply for material name changes
363         ApplyMaterialChanges();
364 }
365
366 /**
367 * Sets the entire dictionary for a material or stage
368 * @param stage The stage or -1 for the material.
369 * @param data The dictionary to copy.
370 */
371 void MaterialDoc::SetData(int stage, idDict* data) {
372         idDict* dict;
373         if(stage == -1) {
374                 dict = &editMaterial.materialData;
375         } else {
376                 assert(stage >= 0 && stage < GetStageCount());
377                 dict = &editMaterial.stages[stage]->stageData;
378         }
379         dict->Clear();
380         dict->Copy(*data);
381 }
382
383 /**
384 * Called when the editor modifies the source of the material.
385 * @param text The new source text.
386 */
387 void MaterialDoc::SourceModify(SourceModifyOwner* owner) {
388         
389         sourceModifyOwner = owner;
390         sourceModify = true;
391         OnMaterialChanged();
392 }
393
394 /**
395 * Returns true if the source text of this material has been edited.
396 */
397 bool MaterialDoc::IsSourceModified() {
398         return sourceModify;
399 }
400
401 /**
402 * Applies any source changes to the edit representation of the material.
403 */
404 void MaterialDoc::ApplySourceModify(idStr& text) {
405         
406         if(sourceModify) {
407                 
408                 //Changes in the source need to clear any undo redo buffer because we have no idea what has changed
409                 manager->ClearUndo();
410                 manager->ClearRedo();
411
412                 ClearEditMaterial();
413
414                 idLexer         src;
415                 src.LoadMemory(text, text.Length(), "Material");
416
417                 src.SetFlags( 
418                         LEXFL_NOSTRINGCONCAT |                  // multiple strings seperated by whitespaces are not concatenated
419                         LEXFL_NOSTRINGESCAPECHARS |             // no escape characters inside strings
420                         LEXFL_ALLOWPATHNAMES |                  // allow path seperators in names
421                         LEXFL_ALLOWMULTICHARLITERALS |  // allow multi character literals
422                         LEXFL_ALLOWBACKSLASHSTRINGCONCAT |      // allow multiple strings seperated by '\' to be concatenated
423                         LEXFL_NOFATALERRORS                             // just set a flag instead of fatal erroring
424                         );
425
426                 idToken token;
427                 if(!src.ReadToken(&token)) {
428                         src.Warning( "Missing decl name" );
429                         return;
430                 }
431                 
432                 ParseMaterial(&src);
433                 sourceModify = false;
434
435                 //Check to see if the name has changed
436                 if(token.Icmp(name)) {
437                         SetMaterialName(token, false);
438                 }
439         }
440 }
441
442 /**
443 * Returns the appropriate source for the editing
444 */
445 const char*     MaterialDoc::GetEditSourceText() {
446
447         return GenerateSourceText();
448 }
449
450 /**
451 * Adds a stage to the material.
452 * @param stageType The type of the stage: normal or special.
453 * @param stageName The name of the stage.
454 * @param addUndo Flag that specifies if the system should add an undo operation.
455 */
456 void MaterialDoc::AddStage(int stageType, const char* stageName, bool addUndo) {
457         MEStage_t* newStage = new MEStage_t();
458
459         int index = editMaterial.stages.Append(newStage);
460         newStage->stageData.Set("name", stageName);
461         newStage->stageData.SetInt("stagetype", stageType);
462         newStage->enabled = true;
463
464         if(addUndo) {
465                 StageInsertModifier* mod = new StageInsertModifier(manager, name, index, stageType, stageName);
466                 manager->AddMaterialUndoModifier(mod);
467         }
468
469         manager->StageAdded(this, index);
470
471         OnMaterialChanged();
472 }
473
474 /**
475 * Inserts a new stage to the material at a specified location.
476 * @param stage The location to insert the stage.
477 * @param stageType The type of the stage: normal or special.
478 * @param stageName The name of the stage.
479 * @param addUndo Flag that specifies if the system should add an undo operation.
480 */
481 void MaterialDoc::InsertStage(int stage, int stageType, const char* stageName, bool addUndo) {
482         MEStage_t* newStage = new MEStage_t();
483
484         editMaterial.stages.Insert(newStage, stage);
485         newStage->stageData.Set("name", stageName);
486         newStage->stageData.SetInt("stagetype", stageType);
487         newStage->enabled = true;
488
489         if(addUndo) {
490                 StageInsertModifier* mod = new StageInsertModifier(manager, name, stage, stageType, stageName);
491                 manager->AddMaterialUndoModifier(mod);
492         }
493
494         manager->StageAdded(this, stage);
495
496         OnMaterialChanged();
497 }
498
499 /**
500 * Removes a stage from the material.
501 * @param stage The stage to remove.
502 * @param addUndo Flag that specifies if the system should add an undo operation.
503 */
504 void MaterialDoc::RemoveStage(int stage, bool addUndo) {
505         assert(stage >= 0 && stage < GetStageCount());
506
507         if(addUndo) {
508                 //Add modifier to undo this operation
509                 StageDeleteModifier* mod = new StageDeleteModifier(manager, name, stage, editMaterial.stages[stage]->stageData);
510                 manager->AddMaterialUndoModifier(mod);
511         }
512
513         //delete the stage and remove it from the list
514         delete editMaterial.stages[stage];
515         editMaterial.stages.RemoveIndex(stage);
516
517         manager->StageDeleted(this, stage);
518
519         OnMaterialChanged();
520 }
521
522 /**
523 * Removes all stages from the material.
524 */
525 void MaterialDoc::ClearStages() {
526
527         //Delete each stage and clear the list
528         for(int i = GetStageCount() - 1; i >= 0; i--) {
529                 RemoveStage(i);
530         }
531 }
532
533 /**
534 * Moves a stage from one location to another.
535 * @param from The original location of the stage.
536 * @param to The new location of the stage.
537 * @param addUndo Flag that specifies if the system should add an undo operation.
538 */
539 void MaterialDoc::MoveStage(int from, int to, bool addUndo) {
540         assert(from >= 0 && from < GetStageCount());
541         assert(to >= 0 && to < GetStageCount());
542
543         int origFrom = from;
544         int origTo = to;
545
546         if(from < to)
547                 to++;
548
549         MEStage_t* pMove = editMaterial.stages[from];
550         editMaterial.stages.Insert(pMove, to);
551
552         if(from > to)
553                 from++;
554
555         editMaterial.stages.RemoveIndex(from);
556
557         manager->StageMoved(this, origFrom, origTo);
558
559         if(addUndo) {
560                 StageMoveModifier *mod = new StageMoveModifier(manager, name, origFrom, origTo);
561                 manager->AddMaterialUndoModifier(mod);
562         }
563
564         OnMaterialChanged();
565 }
566
567 /**
568 * Applies any changes to the material
569 * @param force If true then the material will be applied regardless of the number of changes.
570 */
571 void MaterialDoc::ApplyMaterialChanges(bool force) {
572
573         if(force || applyWaiting) {
574
575                 if(sourceModify && sourceModifyOwner) {
576                         idStr text = sourceModifyOwner->GetSourceText();
577                         ApplySourceModify(text);
578                 }
579
580                 ReplaceSourceText();
581
582                 char *declText = (char *) _alloca( renderMaterial->GetTextLength() + 1 );
583                 renderMaterial->GetText( declText );
584
585                 renderMaterial->GetText(declText);
586                 
587                 ParseMaterialText(declText);
588
589                 applyWaiting = false;
590
591                 assert(manager);
592                 manager->MaterialApplied(this);
593         }
594 }
595
596 /**
597 * Saves the material.
598 */
599 void MaterialDoc::Save() {
600
601         EnableAllStages(true);
602
603         //Apply the material so that the renderMaterial has the source text
604         if(!deleted) {
605                 ApplyMaterialChanges(true);
606         } else {
607                 //Replace the text with nothing
608                 renderMaterial->SetText(" ");
609         }
610
611         if(renderMaterial->Save()) {
612
613                 modified = false;
614
615                 //Notify the world
616                 assert(manager);
617                 manager->MaterialSaved(this);
618         } else {
619                 MessageBox(GetMaterialEditorWindow(), va("Unable to save '%s'. It may be read-only", name.c_str()), "Save Error", MB_OK | MB_ICONERROR);
620         }
621 }
622
623 /**
624 * Deletes the material.
625 */
626 void MaterialDoc::Delete() {
627         deleted = true;
628
629         OnMaterialChanged();
630 }
631
632 /**
633 * Sets the proper internal states and notifies the MaterialDocManager once a material has been changed.
634 */
635 void MaterialDoc::OnMaterialChanged() {
636
637         modified = true;
638         applyWaiting = true;
639
640         assert(manager);
641         manager->MaterialChanged(this);
642 }
643
644 /**
645 * Passes text to a render material for parsing.
646 * @param source The text that sould be applied to the idMaterial.
647 */
648 void MaterialDoc::ParseMaterialText(const char* source) {
649
650         /*idLexer src;
651         src.LoadMemory(source, strlen(source), "material");
652         src.SetFlags( 
653                 LEXFL_NOSTRINGCONCAT |                  // multiple strings seperated by whitespaces are not concatenated
654                 LEXFL_NOSTRINGESCAPECHARS |             // no escape characters inside strings
655                 LEXFL_ALLOWPATHNAMES |                  // allow path seperators in names
656                 LEXFL_ALLOWMULTICHARLITERALS |  // allow multi character literals
657                 LEXFL_ALLOWBACKSLASHSTRINGCONCAT |      // allow multiple strings seperated by '\' to be concatenated
658                 LEXFL_NOFATALERRORS                             // just set a flag instead of fatal erroring
659                 );
660
661         //Skip the name becuase the material parsing code expects it
662         src.SkipUntilString("{");*/
663
664         //Now let the material parse the text
665         renderMaterial->Parse(source, strlen(source));
666 }
667
668 /**
669 * Parses the source text from an idMaterial and initializes the editor dictionary representation
670 * of the material.
671 * @param src The idLexer object that contains the material text.
672 */
673 void MaterialDoc::ParseMaterial(idLexer* src) {
674
675         idToken         token;
676
677         //Parse past the name
678         src->SkipUntilString("{");
679         
680         while ( 1 ) {
681                 if ( !src->ExpectAnyToken( &token ) ) {
682                         //Todo: Add some error checking here
683                         return;
684                 }
685
686                 if ( token == "}" ) {
687                         break;
688                 }
689
690                 if(ParseMaterialDef(&token, src, MaterialDefManager::MATERIAL_DEF_MATERIAL, &editMaterial.materialData)) {
691                         continue;
692                 }
693                 
694                 if ( !token.Icmp( "diffusemap" ) ) {
695                         //Added as a special stage
696                         idStr str;
697                         src->ReadRestOfLine( str );
698                         AddSpecialMapStage("diffusemap", str);
699                 }
700                 else if ( !token.Icmp( "specularmap" ) ) {
701                         idStr str;
702                         src->ReadRestOfLine( str );
703                         AddSpecialMapStage("specularmap", str);
704                 }
705                 else if ( !token.Icmp( "bumpmap" ) ) {
706                         idStr str;
707                         src->ReadRestOfLine( str );
708                         AddSpecialMapStage("bumpmap", str);
709                 }
710                 else if( token == "{" ) {
711                         ParseStage(src);
712                 }
713         }
714 }
715
716 /**
717 * Parses a single stage from the source text from an idMaterial and initializes the editor dictionary 
718 * representation of the material.
719 * @param src The idLexer object that contains the material text.
720 */
721 void MaterialDoc::ParseStage(idLexer* src) {
722         
723         MEStage_t* newStage = new MEStage_t();
724         int index = editMaterial.stages.Append(newStage);
725         
726         newStage->stageData.SetInt("stagetype", STAGE_TYPE_NORMAL);
727         newStage->enabled = true;
728
729         idToken         token;
730
731         while ( 1 ) {
732                 
733                 if ( !src->ExpectAnyToken( &token ) ) {
734                         //Todo: Add some error checking here
735                         return;
736                 }
737
738                 if ( token == "}" ) {
739                         break;
740                 }
741
742                 if(ParseMaterialDef(&token, src, MaterialDefManager::MATERIAL_DEF_STAGE, &newStage->stageData)) {
743                         continue;
744                 }
745
746                 if(!token.Icmp("name")) {
747
748                         idStr str;
749                         src->ReadRestOfLine( str );
750                         str.StripTrailing('\"');
751                         str.StripLeading('\"');
752                         newStage->stageData.Set("name", str);
753                         continue;
754                 }
755         }
756
757         idStr name;
758         newStage->stageData.GetString("name", "", name);
759         if(name.Length() <= 0)
760                 newStage->stageData.Set("name", va("Stage %d", index+1));
761
762 }
763
764 /**
765 * Adds a special stage to the material.
766 * @param stageName The name of the special stage bumpmap, diffusemap or specularmap
767 * @param map The map for the special stage.
768 */
769 void MaterialDoc::AddSpecialMapStage(const char* stageName, const char* map) {
770         MEStage_t* newStage = new MEStage_t();
771         int index = editMaterial.stages.Append(newStage);
772         newStage->stageData.Set("name", stageName);
773         newStage->stageData.Set("map", map);
774         newStage->stageData.SetInt("stagetype", STAGE_TYPE_SPECIALMAP);
775         newStage->enabled = true;
776 }
777
778 /**
779 * Finds the appropriate material definition for the supplied token and initializes the
780 * internal dictionary data.
781 * @param token The token to lookup
782 * @param src The idLexer that contains the material source text.
783 * @param type The type of attribute grouping to use material, stage or special stage.
784 * @param dict The dictionary to initialize.
785 */
786 bool MaterialDoc::ParseMaterialDef(idToken* token, idLexer* src, int type, idDict* dict) {
787         
788         MaterialDefList* defs = MaterialDefManager::GetMaterialDefs(type);
789
790         for(int i = 0; i < defs->Num(); i++) {
791                 if(!token->Icmp((*defs)[i]->dictName)) {
792
793                         switch((*defs)[i]->type) {
794                                         case MaterialDef::MATERIAL_DEF_TYPE_STRING:
795                                                 {
796                                                         idStr str;
797                                                         src->ReadRestOfLine( str );
798                                                         if((*defs)[i]->quotes) {
799                                                                 str.StripTrailing('\"');
800                                                                 str.StripLeading('\"');
801                                                         }
802                                                         dict->Set((*defs)[i]->dictName, str);
803                                                 }
804                                                 break;
805                                         case MaterialDef::MATERIAL_DEF_TYPE_BOOL:
806                                                 {
807                                                         src->SkipRestOfLine();
808                                                         dict->SetBool((*defs)[i]->dictName, true);
809                                                 }
810                                                 break;
811                                         case MaterialDef::MATERIAL_DEF_TYPE_FLOAT:
812                                                 {
813                                                         idStr str;
814                                                         src->ReadRestOfLine( str );
815                                                         dict->Set((*defs)[i]->dictName, str);
816                                                 }
817                                                 break;
818                                         case MaterialDef::MATERIAL_DEF_TYPE_INT:
819                                                 {
820                                                         idStr str;
821                                                         src->ReadRestOfLine( str );
822                                                         dict->Set((*defs)[i]->dictName, str);
823                                                 }
824                                                 break;
825                         }
826                         return true;
827                 }
828         }
829         return false;
830 }
831
832 /**
833 * Cleans up the edit material by deleting the stage data structures.
834 */
835 void MaterialDoc::ClearEditMaterial() {
836
837         for(int i = 0; i < GetStageCount(); i++) {
838                 delete editMaterial.stages[i];
839         }
840         editMaterial.stages.Clear();
841         editMaterial.materialData.Clear();
842 }
843
844 /**
845 * Writes the internal dictionary data to the standard format.
846 */
847 const char*     MaterialDoc::GenerateSourceText() {
848
849         idFile_Memory f;
850
851         f.WriteFloatString("\n\n/*\n"
852                 "\tGenerated by the Material Editor.\n"
853                 "\tType 'materialeditor' at the console to launch the material editor.\n"
854                 "*/\n" );
855
856         f.WriteFloatString("%s\n", name.c_str());
857         f.WriteFloatString( "{\n" );
858         WriteMaterialDef(-1, &f, MaterialDefManager::MATERIAL_DEF_MATERIAL, 1);
859
860         for(int i = 0; i < editMaterial.stages.Num(); i++) {
861                 if(editMaterial.stages[i]->enabled) {
862                         WriteStage(i, &f);
863                 }
864         }
865
866         f.WriteFloatString( "}\n" );
867
868         return f.GetDataPtr();
869
870 }
871
872 /**
873 * Writes the internal dictionary data to the standard format and replaces the 
874 * idMaterial source text with the newly generated text.
875 */
876 void MaterialDoc::ReplaceSourceText() {
877                 renderMaterial->SetText(GenerateSourceText());
878 }
879
880 /**
881 * Writes a single stage.
882 * @param stage The stage to write.
883 * @param file The file where the stage should be wirtten
884 */
885 void MaterialDoc::WriteStage(int stage, idFile_Memory* file) {
886
887         //idStr stageName = GetAttribute(stage, "name");
888         int type = GetAttributeInt(stage, "stagetype");
889         //if(!stageName.Icmp("diffusemap") || !stageName.Icmp("specularmap") || !stageName.Icmp("bumpmap")) {
890         if(type == STAGE_TYPE_SPECIALMAP) {
891                 WriteSpecialMapStage(stage, file);
892                 return;
893         }
894
895         file->WriteFloatString( "\t{\n" );
896         idStr name = GetAttribute(stage, "name");
897         if(name.Length() > 0) {
898                 file->WriteFloatString("\t\tname\t\"%s\"\n", name.c_str());
899         }
900         WriteMaterialDef(stage, file, MaterialDefManager::MATERIAL_DEF_STAGE, 2);
901         file->WriteFloatString( "\t}\n" );
902         
903 }
904
905 /**
906 * Writes a single special stage.
907 * @param stage The stage to write.
908 * @param file The file where the stage should be wirtten
909 */
910 void MaterialDoc::WriteSpecialMapStage(int stage, idFile_Memory* file) {
911         idStr stageName = GetAttribute(stage, "name");
912         idStr map = GetAttribute(stage, "map");
913
914         file->WriteFloatString( "\t%s\t%s\n", stageName.c_str(), map.c_str() );
915 }
916
917 /**
918 * Writes a set of material attributes to a file.
919 * @param stage The stage to write or -1 for the material.
920 * @param file The file where the stage should be wirtten.
921 * @param type The attribute grouping to use.
922 * @param indent The number of tabs to indent the text.
923 */
924 void MaterialDoc::WriteMaterialDef(int stage, idFile_Memory* file, int type, int indent) {
925
926         idStr prefix = "";
927         for(int i = 0; i < indent; i++) {
928                 prefix += "\t";
929         }
930
931         MaterialDefList* defs = MaterialDefManager::GetMaterialDefs(type);
932         for(int i = 0; i < defs->Num(); i++) {
933                 switch((*defs)[i]->type) {
934                         case MaterialDef::MATERIAL_DEF_TYPE_STRING:
935                                 {
936                                         idStr attrib = GetAttribute(stage, (*defs)[i]->dictName);
937                                         if(attrib.Length() > 0) {
938                                                 if((*defs)[i]->quotes)
939                                                         file->WriteFloatString("%s%s\t\"%s\"\n", prefix.c_str(), (*defs)[i]->dictName.c_str(), attrib.c_str());
940                                                 else
941                                                         file->WriteFloatString("%s%s\t%s\n", prefix.c_str(), (*defs)[i]->dictName.c_str(), attrib.c_str());
942                                         }
943                                 }
944                                 break;
945                         case MaterialDef::MATERIAL_DEF_TYPE_BOOL:
946                                 {
947                                         if(GetAttributeBool(stage, (*defs)[i]->dictName))
948                                                 file->WriteFloatString("%s%s\t\n",prefix.c_str(), (*defs)[i]->dictName.c_str());
949                                 }
950                                 break;
951                         case MaterialDef::MATERIAL_DEF_TYPE_FLOAT:
952                                 {
953                                         float val = GetAttributeFloat(stage, (*defs)[i]->dictName);
954                                         file->WriteFloatString("%s%s\t%f\n", prefix.c_str(), (*defs)[i]->dictName.c_str(), val);
955                                 }
956                                 break;
957                         case MaterialDef::MATERIAL_DEF_TYPE_INT:
958                                 {
959                                         int val = GetAttributeInt(stage, (*defs)[i]->dictName);
960                                         file->WriteFloatString("%s%s\t%d\n", prefix.c_str(), (*defs)[i]->dictName.c_str(), val);
961                                 }
962                                 break;
963                 }
964         }
965 }
966