00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include <stdexcept>
00014 #include <iostream>
00015
00016 #include "World/Scene.hh"
00017
00018 namespace World {
00020 template<typename T>
00021 static std::string ToStr(const T &var) {
00022 std::ostringstream tmp;
00023 tmp << var;
00024 return tmp.str();
00025 }
00026
00028 static Double ToDouble(const std::string &str)
00029 {
00030 std::stringstream os(str);
00031 Double a;
00033 os >> a;
00034 return a;
00035 }
00036
00038 class XMLError : public std::runtime_error {
00039 public:
00041 XMLError(xmlNodePtr Node, const std::string &Desc)
00042 : std::runtime_error(
00043 "On line " + ToStr(xmlGetLineNo(Node)) +
00044 ": " + Desc)
00045 {
00046 }
00047
00049 XMLError(const std::string &Desc)
00050 : std::runtime_error(Desc)
00051 {
00052 }
00053 };
00054
00057 static Bool IsDouble(const std::string &str)
00058 {
00059 Bool WasDot = false;
00060 if (str.length() == 0)
00061 return false;
00062 for (UInt i = 0U;
00063 i != str.length();
00064 i++) {
00065 if (str[i] == '-' && i == 0)
00066 continue;
00067
00068 if (str[i] == '.' && WasDot == false) {
00069 WasDot = true;
00070 continue;
00071 }
00072 if (str[i] < '0' || str[i] > '9')
00073 return false;
00074 }
00075 return true;
00076 }
00077
00079 static std::string GetProp(xmlNodePtr Node, const char *id)
00080 {
00081 xmlChar *text = (xmlChar *)xmlGetProp(Node,(const xmlChar *)id);
00082 if (text == NULL)
00083 return std::string("");
00084 std::string t((const char *)text);
00085 xmlFree(text);
00086 return t;
00087 }
00088
00090 static Double GetDoubleProp(xmlNodePtr Node, const char *str)
00091 {
00092 std::string Prop = GetProp(Node, str);
00093 if (Prop == "")
00094 throw XMLError(Node,
00095 "Unable to find numerical tag " + std::string(str));
00096 if (!IsDouble(Prop))
00097 throw XMLError(Node, "Invalid numerical value");
00098 Double Val = ToDouble(Prop);
00099
00100 return Val;
00101 }
00102
00105 static Double GetDoubleProp(xmlNodePtr Node,
00106 const char *str,
00107 Double DefaultValue)
00108 {
00109 std::string Prop = GetProp(Node, str);
00110 if (Prop == "")
00111 return DefaultValue;
00112
00113 Double Val = ToDouble(Prop);
00114
00115 return Val;
00116 }
00117
00119 static void EnsureColorTag(xmlNodePtr Node, xmlNodePtr ErrorCtx = NULL)
00120 {
00121 if (!Node) {
00122 throw XMLError(ErrorCtx,
00123 "Expected \"Color\" tag within"
00124 " this context got nothing ");
00125 }
00126
00127 if (xmlStrcmp(Node->name, (const xmlChar *) "Color"))
00128 throw XMLError(Node,
00129 "Expected \"Color\" tag got " +
00130 std::string((char *)Node->name));
00131 }
00132
00136 static std::string GetID(xmlNodePtr Node)
00137 {
00138 std::string id = GetProp(Node, "id");
00139 if (id == "")
00140 throw XMLError(Node,
00141 "No identifier (id tag) specified");
00142 return id;
00143 }
00144
00146 static Bool IsToken(xmlNodePtr Node, const char *Token)
00147 {
00148 if ((xmlStrcmp(Node->name, (const xmlChar *)Token)) == 0)
00149 return true;
00150 return false;
00151 }
00152
00154 static void OmitComments(xmlNodePtr &Node)
00155 {
00156 for (; Node != NULL; Node = Node->next) {
00157 if (IsToken(Node, "comment"))
00158 continue;
00159 break;
00160 }
00161 }
00162
00163 void Scene::DumpLibrary()
00164 {
00165 using namespace std;
00166 cout << "--- Dumping colors: " << endl;
00167 for (ColIter i = ColMap.begin();
00168 i != ColMap.end(); i++) {
00169 cout << i->first << " == " << i->second << endl;
00170 }
00171
00172 cout << "--- Dumping textures: " << endl;
00173 for (TexIter i = TexMap.begin();
00174 i != TexMap.end(); i++) {
00175 cout << i->first << " == " << *i->second << endl;
00176 }
00177
00178 cout << "--- Dumping materials: " << endl;
00179 for (MatIter i = MatMap.begin();
00180 i != MatMap.end(); i++) {
00181 cout << i->first << " == " << *i->second << endl;
00182 }
00183 }
00184
00185 void Scene::CreateLibrary()
00186 {
00187 using namespace std;
00188
00189 ColMap.insert(make_pair(string("Black"), ColLib::Black()));
00190 ColMap.insert(make_pair(string("White"), ColLib::White()));
00191 ColMap.insert(make_pair(string("Red"), ColLib::Red()));
00192 ColMap.insert(make_pair(string("Green"), ColLib::Green()));
00193 ColMap.insert(make_pair(string("Blue"), ColLib::Blue()));
00194 ColMap.insert(make_pair(string("Gray"), ColLib::Gray()));
00195
00196 TexMap.insert(make_pair(string("Black"), &TexLib::Black()));
00197 TexMap.insert(make_pair(string("White"), &TexLib::White()));
00198 TexMap.insert(make_pair(string("Red"), &TexLib::Red()));
00199 TexMap.insert(make_pair(string("Green"), &TexLib::Green()));
00200 TexMap.insert(make_pair(string("Blue"), &TexLib::Blue()));
00201 TexMap.insert(make_pair(string("Gray"), &TexLib::Gray()));
00202
00203 IdxMap.insert(make_pair(string("Vacuum"), 1.0));
00204 IdxMap.insert(make_pair(string("Air"), 1.0002926));
00205 IdxMap.insert(make_pair(string("Water"), 1.333));
00206 IdxMap.insert(make_pair(string("Diamond"), 2.419));
00207 IdxMap.insert(make_pair(string("Amber"), 1.55));
00208 IdxMap.insert(make_pair(string("Salt"), 1.544));
00209 IdxMap.insert(make_pair(string("Ice"), 1.31));
00210 IdxMap.insert(make_pair(string("Glass"), 1.60));
00211 }
00212
00213 Color Scene::ParseColor(xmlNodePtr Node)
00214 {
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225 std::string id = GetProp(Node, "id");
00226 std::string r = GetProp(Node, "r");
00227 std::string g = GetProp(Node, "g");
00228 std::string b = GetProp(Node, "b");
00229
00230 if (id == "" && (r == "" || g == "" || b == ""))
00231 throw XMLError(Node,
00232 "Inappropriate color declaration");
00233
00234 if (id != "") {
00235
00236 ColIter iter = ColMap.find(id);
00237 if (iter != ColMap.end())
00238 return iter->second;
00239
00240 if (r == "" || g == "" || b == "")
00241 throw XMLError(Node,
00242 "Color \"" + id +
00243 "\" doesn't exist in database");
00244 }
00245
00246
00247 double R, G, B;
00248 R = ToDouble(r);
00249 G = ToDouble(g);
00250 B = ToDouble(b);
00251 if (R > 1.0 || R < 0.0 || G > 1.0
00252 || G < 0.0 || B > 1.0 || B < 0.0)
00253 throw XMLError(Node,
00254 "Illegal color attribute value. "
00255 "R, G, B belongs between 0.0 and 1.0");
00256 if (id != "")
00257 ColMap.insert( make_pair(id, Color(R, G, B)) );
00258 return Color(R, G, B);
00259 }
00260
00261 Math::Vector Scene::ParseVector(xmlNodePtr Node)
00262 {
00263 std::string x = GetProp(Node, "x");
00264 std::string y = GetProp(Node, "y");
00265 std::string z = GetProp(Node, "z");
00266 if (x == "" || y == "" || z == "")
00267 throw XMLError(Node,
00268 "Inappropriate vector declaration");
00269
00270 double X, Y, Z;
00271 X = ToDouble(x);
00272 Y = ToDouble(y);
00273 Z = ToDouble(z);
00274 return Math::Vector(X, Y, Z);
00275 }
00276
00277 const Texture *Scene::GetTexture(const std::string &id)
00278 {
00279 TexIter iter = TexMap.find(id);
00280 if (iter != TexMap.end())
00281 return iter->second;
00282 return NULL;
00283 }
00284
00285 const Material *Scene::GetMaterial(const std::string &id)
00286 {
00287 MatIter iter = MatMap.find(id);
00288 if (iter != MatMap.end())
00289 return iter->second;
00290 return NULL;
00291 }
00292
00293
00294 void Scene::ParseTexture(xmlNodePtr Node)
00295 {
00296 std::string id = GetID(Node);
00297 std::string Type = GetProp(Node, "type");
00298 if (Type == "")
00299 throw XMLError(Node,
00300 "No texture type specified"
00301 " (plane or checked)");
00302 if (id == "")
00303 throw XMLError(Node,
00304 "No identified given for texture");
00305
00306 if (GetTexture(id) != NULL)
00307 throw XMLError(Node,
00308 "Texture with this ID already exists");
00309
00310 if (Type == "Plain") {
00311 xmlNodePtr Child = Node->xmlChildrenNode;
00312 OmitComments(Child);
00313 EnsureColorTag(Child, Node);
00314 Color C = ParseColor(Child);
00315
00316 Child = Child->next;
00317 OmitComments(Child);
00318 if (Child)
00319 throw XMLError(Node,
00320 "Plane texture requires"
00321 " one color parameter");
00322
00323 TexLib::Plain *Tex = new TexLib::Plain(C);
00324
00325
00326 this->AddTexture(Tex);
00327 TexMap.insert( make_pair(id, Tex) );
00328
00329 } else if (Type == "Checked") {
00330 xmlNodePtr Child = Node->xmlChildrenNode;
00331 OmitComments(Child);
00332
00333 EnsureColorTag(Child, Node);
00334 Color A = ParseColor(Child);
00335
00336
00337 Child = Child->next;
00338 OmitComments(Child);
00339 EnsureColorTag(Child, Node);
00340 Color B = ParseColor(Child);
00341
00342 Child = Child->next;
00343 OmitComments(Child);
00344 if (Child)
00345 throw XMLError(Node,
00346 "Checked texture requires"
00347 " two color parameter");
00348
00349
00350 std::string Width_ = GetProp(Node, "width");
00351 std::string Height_ = GetProp(Node, "height");
00352 std::string Tile_ = GetProp(Node, "tile");
00353 Double Width, Height;
00354 Bool Tile = true;
00355 if (Width_ == "")
00356 Width = 1.0;
00357 else Width = ToDouble(Width_);
00358 if (Height_ == "")
00359 Height = 1.0;
00360 else Height = ToDouble(Height_);
00361 if (Tile_ == "false" || Tile_ == "no")
00362 Tile = false;
00363
00364 TexLib::Checked *Tex =
00365 new TexLib::Checked(A, B, Width, Height, Tile);
00366 this->AddTexture(Tex);
00367
00368 TexMap.insert( make_pair(id, Tex) );
00369 } else
00370 throw XMLError(Node,
00371 "Invalid texture type specified");
00372 }
00373
00374 Double Scene::ParseIdx(xmlNodePtr Node)
00375 {
00376 std::string idx = GetProp(Node, "idx");
00377 if (IsDouble(idx)) {
00378 Double idx = GetDoubleProp(Node, "idx");
00379 return idx;
00380 } else {
00381 if (idx != "") {
00382 IdxIter iter = IdxMap.find(idx);
00383 if (iter == IdxMap.end())
00384 throw XMLError(Node,
00385 "No refractive index"
00386 " with that name");
00387 return iter->second;
00388 } else {
00389 return MatLib::IdxGlass;
00390 }
00391 }
00392 }
00393
00394 void Scene::ParseMaterial(xmlNodePtr Node)
00395 {
00396 std::string id = GetProp(Node, "id");
00397 if (id == "")
00398 throw XMLError("Material identifier not specified");
00399 if (GetMaterial(id) != NULL)
00400 throw XMLError("Material already defined");
00401
00402
00403 const Texture
00404 *Diffuse = &TexLib::Green(),
00405 *Specular = &TexLib::White(),
00406 *Reflect = &TexLib::Black(),
00407 *Refract = &TexLib::Black();
00408
00409 std::string diffuse = GetProp(Node, "diffuse");
00410 std::string specular = GetProp(Node, "specular");
00411 std::string reflect = GetProp(Node, "reflect");
00412 std::string refract = GetProp(Node, "refract");
00413 Double Shininess = GetDoubleProp(Node, "shininess", 12.0);
00414 Double Idx = ParseIdx(Node);
00415
00416
00417 if (diffuse != "") Diffuse = GetTexture(diffuse);
00418 if (specular != "") Specular = GetTexture(specular);
00419 if (reflect != "") Reflect = GetTexture(reflect);
00420 if (refract != "") Refract = GetTexture(refract);
00421
00422 if (!Diffuse) throw XMLError(Node,
00423 "Undefined texture " + diffuse);
00424 if (!Specular) throw XMLError(Node,
00425 "Undefined texture " + specular);
00426 if (!Reflect) throw XMLError(Node,
00427 "Undefined texture " + reflect);
00428 if (!Refract) throw XMLError(Node,
00429 "Undefined texture " + refract);
00430
00431
00432 Material *Mat = new Material(
00433 *Diffuse, *Specular,
00434 *Refract, *Reflect,
00435 0.0, 0.0, 0.0,
00436 Shininess, Idx);
00437
00438 MatMap.insert( make_pair(id, Mat) );
00439 this->AddMaterial(Mat);
00440 }
00441
00442 void Scene::ParseLight(xmlNodePtr Node)
00443 {
00444 std::string Type = GetProp(Node, "type");
00445 if (Type == "")
00446 throw XMLError("Light type not given");
00447 if (Type == "Ambient") {
00448 xmlNodePtr Cur = Node->xmlChildrenNode;
00449 OmitComments(Cur);
00450 EnsureColorTag(Cur, Node);
00451 Color C = ParseColor(Cur);
00452 Cur = Cur->next;
00453 OmitComments(Cur);
00454 if (Cur)
00455 throw XMLError(Node,
00456 "Garbage after color declaration");
00457
00458 this->AddLight(new AmbientLight(C));
00459 } else if (Type == "Point") {
00460 xmlNodePtr Cur = Node->xmlChildrenNode;
00461 Bool GotColor = false;
00462 Bool GotPosition = false;
00463 Color C;
00464 Math::Vector Pos;
00465 OmitComments(Cur);
00466 for (; Cur != NULL;
00467 Cur = Cur->next, OmitComments(Cur)) {
00468 if (!GotPosition && IsToken(Cur, "Position")) {
00469 Pos = ParseVector(Cur);
00470 GotPosition = true;
00471 }
00472 else if (!GotColor && IsToken(Cur, "Color")) {
00473 C = ParseColor(Cur);
00474 GotColor = true;
00475 } else {
00476 throw XMLError(Cur,
00477 "Garbage in pointlight"
00478 " declaration");
00479 }
00480 }
00481 if (!GotColor || !GotPosition)
00482 throw XMLError(Node,
00483 "Point light requires color"
00484 " and position data");
00485 this->AddLight(new PointLight(Pos, C));
00486 }
00487 }
00488
00489 void Scene::ParseCamera(xmlNodePtr Node)
00490 {
00491 xmlNodePtr Cur = Node->xmlChildrenNode;
00492 Bool
00493 GotPos = false,
00494 GotDir = false,
00495 GotTop = false;
00496
00497 Math::Vector
00498 Pos(0.0, 0.0, 0.0),
00499 Dir(0.0, 0.0, 1.0),
00500 Top(0.0, 1.0, 0.0);
00501 Double FOV = GetDoubleProp(Node, "FOV", 45.0);
00502
00503 OmitComments(Cur);
00504 for (; Cur != NULL; Cur = Cur->next, OmitComments(Cur)) {
00505 if (!GotPos && IsToken(Cur, "Pos")) {
00506 Pos = ParseVector(Cur);
00507 GotPos = true;
00508 } else
00509 if (!GotDir && IsToken(Cur, "Dir")) {
00510 Dir = ParseVector(Cur);
00511 GotDir = true;
00512 } else
00513 if (!GotTop && IsToken(Cur, "Top")) {
00514 Top = ParseVector(Cur);
00515 GotTop = true;
00516 } else
00517 throw XMLError(
00518 "Garbage in camera"
00519 " declaration");
00520 }
00521
00522
00523 this->C = Camera(Pos,
00524 Dir,
00525 Camera::DegreeToFOV(FOV),
00526 !GotTop,
00527 Top);
00528
00529 }
00530
00531 void Scene::ParseSphere(xmlNodePtr Node)
00532 {
00533 xmlNodePtr Cur = Node->xmlChildrenNode;
00534 Bool GotPosition = false,
00535 GotMaterial = false;
00536 const Material *Material = &MatLib::Gray();;
00537 Math::Vector Position(0.0, 0.0, 0.0);
00538 Double Radius = GetDoubleProp(Node, "radius");
00539
00540 OmitComments(Cur);
00541 for (; Cur != NULL; Cur = Cur->next, OmitComments(Cur)) {
00542 if (!GotPosition && IsToken(Cur, "Position")) {
00543 Position = ParseVector(Cur);
00544 GotPosition = true;
00545 } else
00546 if (!GotMaterial && IsToken(Cur, "Material")) {
00547 std::string id = GetProp(Cur, "id");
00548 Material = GetMaterial(id);
00549 if (!Material)
00550 throw XMLError(Cur,
00551 "Material doesn't exist");
00552 GotMaterial = true;
00553 } else
00554 throw XMLError(
00555 "Garbage in Sphere"
00556 " declaration");
00557 }
00558
00559
00560 Sphere *S = new Sphere(Position, Radius, *Material);
00561 if (DEBUG)
00562 std::cout
00563 << "Adding sphere " << *S << std::endl;
00564 this->AddObject(S);
00565
00566 }
00567
00568 void Scene::ParsePlane(xmlNodePtr Node)
00569 {
00570 xmlNodePtr Cur = Node->xmlChildrenNode;
00571 Bool GotNormal = false,
00572 GotMaterial = false;
00573 const Material *Material = &MatLib::Gray();;
00574 Math::Vector Normal(0.0, 1.0, 0.0);
00575 Double Distance = GetDoubleProp(Node, "distance");
00576
00577 OmitComments(Cur);
00578 for (; Cur != NULL; Cur = Cur->next, OmitComments(Cur)) {
00579 if (!GotNormal && IsToken(Cur, "Normal")) {
00580 Normal = ParseVector(Cur);
00581 GotNormal = true;
00582 } else
00583 if (!GotMaterial && IsToken(Cur, "Material")) {
00584 std::string id = GetProp(Cur, "id");
00585 Material = GetMaterial(id);
00586 if (!Material)
00587 throw XMLError(Cur,
00588 "Material doesn't exist");
00589 GotMaterial = true;
00590 } else
00591 throw XMLError(
00592 "Garbage in Plane"
00593 " declaration");
00594 }
00595
00596
00597 this->AddObject(
00598 new Plane(Normal, Distance, *Material));
00599 }
00600
00601 Bool Scene::ParseFile(const std::string &File)
00602 {
00603 xmlDocPtr doc = NULL;
00604 xmlNodePtr cur;
00605 xmlLineNumbersDefault(1);
00606 xmlKeepBlanksDefault(0);
00607 try {
00608 doc = xmlParseFile(File.c_str());
00609 if (doc == NULL)
00610 throw XMLError("Unable to initialize parsing");
00611
00612 cur = xmlDocGetRootElement(doc);
00613 if (cur == NULL)
00614 throw XMLError("Document is empty");
00615
00616 if (xmlStrcmp(cur->name, (const xmlChar *) "Scene"))
00617 throw XMLError("Document has wrong type,"
00618 " root node != Scene");
00619
00620
00621 ColMap.clear();
00622 MatMap.clear();
00623 TexMap.clear();
00624 CreateLibrary();
00625
00626 cur = cur->xmlChildrenNode;
00627 OmitComments(cur);
00628 for (; cur != NULL;
00629 cur = cur->next, OmitComments(cur)) {
00630 if (IsToken(cur, "Background")) {
00631 this->Background = ParseColor(cur);
00632 continue;
00633 }
00634
00635 if (IsToken(cur, "Atmosphere")) {
00636 this->AtmosphereIdx = ParseIdx(cur);
00637 continue;
00638 }
00639
00640 if (IsToken(cur, "Color")) {
00641 ParseColor(cur);
00642 continue;
00643 }
00644
00645 if (IsToken(cur, "Texture")) {
00646 ParseTexture(cur);
00647 continue;
00648 }
00649
00650 if (IsToken(cur, "Material")) {
00651 ParseMaterial(cur);
00652 continue;
00653 }
00654
00655 if (IsToken(cur, "Light")) {
00656 ParseLight(cur);
00657 continue;
00658 }
00659
00660 if (IsToken(cur, "Camera")) {
00661 ParseCamera(cur);
00662 continue;
00663 }
00664
00665 if (IsToken(cur, "Sphere")) {
00666 ParseSphere(cur);
00667 continue;
00668 }
00669
00670 if (IsToken(cur, "Plane")) {
00671 ParsePlane(cur);
00672 continue;
00673 }
00674
00675 if (IsToken(cur, "Dump")) {
00676 std::cout << "*** Dump requested ***" << std::endl;
00677 DumpLibrary();
00678 continue;
00679 }
00680
00681 throw XMLError("Invalid token in file \""
00682 + ToStr(cur->name) + "\"");
00683 }
00684 xmlFreeDoc(doc);
00685 return true;
00686 } catch (std::exception &e) {
00687 xmlFreeDoc(doc);
00688 std::cout << "*** Error while parsing XML file:" << std::endl
00689 << e.what() << std::endl;
00690 if (DEBUG) DumpLibrary();
00691 return false;
00692 }
00693
00694 }
00695 }