erebus

annotate liberebus/src/scene.cc @ 43:ed18af9da8f7

first attempt at separating direct from indirect failed miserably
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 11 Jun 2014 16:38:11 +0300
parents 9d6368850fe1
children c4d48a21bc4a
rev   line source
nuclear@19 1 #include <stdio.h>
nuclear@19 2 #include <stdlib.h>
nuclear@19 3 #include <string.h>
nuclear@4 4 #include "scene.h"
nuclear@19 5 #include "snode.h"
nuclear@19 6 #include "object.h"
nuclear@19 7 #include "geomobj.h"
nuclear@4 8
nuclear@8 9 // default enviromental parameters
nuclear@8 10 Environment::Environment()
nuclear@19 11 : bgcolor(0.01, 0.01, 0.01), ambient(0.01, 0.01, 0.01)
nuclear@8 12 {
nuclear@8 13 }
nuclear@8 14
nuclear@4 15 Scene::Scene()
nuclear@4 16 {
nuclear@4 17 active_cam = 0;
nuclear@4 18 root = new SceneNode;
nuclear@4 19 }
nuclear@4 20
nuclear@4 21 Scene::~Scene()
nuclear@4 22 {
nuclear@40 23 clear();
nuclear@40 24 }
nuclear@40 25
nuclear@40 26 void Scene::clear()
nuclear@40 27 {
nuclear@4 28 for(auto obj : objects) {
nuclear@4 29 delete obj;
nuclear@4 30 }
nuclear@40 31 objects.clear();
nuclear@40 32
nuclear@4 33 for(auto node : nodes) {
nuclear@4 34 delete node;
nuclear@4 35 }
nuclear@40 36 nodes.clear();
nuclear@40 37
nuclear@4 38 delete root;
nuclear@40 39 root = 0;
nuclear@40 40
nuclear@40 41 active_cam = 0;
nuclear@4 42 }
nuclear@4 43
nuclear@8 44 void Scene::set_env(const Environment &env)
nuclear@8 45 {
nuclear@8 46 this->env = env;
nuclear@8 47 }
nuclear@8 48
nuclear@8 49 Environment &Scene::get_env()
nuclear@8 50 {
nuclear@8 51 return env;
nuclear@8 52 }
nuclear@8 53
nuclear@8 54 const Environment &Scene::get_env() const
nuclear@8 55 {
nuclear@8 56 return env;
nuclear@8 57 }
nuclear@8 58
nuclear@8 59 Color Scene::get_env_color() const
nuclear@8 60 {
nuclear@8 61 return env.bgcolor;
nuclear@8 62 }
nuclear@8 63
nuclear@8 64 Color Scene::get_env_color(const Ray &ray) const
nuclear@8 65 {
nuclear@8 66 // TODO
nuclear@8 67 return get_env_color();
nuclear@8 68 }
nuclear@8 69
nuclear@4 70 void Scene::add_object(Object *obj)
nuclear@4 71 {
nuclear@4 72 objects.push_back(obj);
nuclear@4 73 }
nuclear@4 74
nuclear@4 75 int Scene::get_object_count() const
nuclear@4 76 {
nuclear@4 77 return (int)objects.size();
nuclear@4 78 }
nuclear@4 79
nuclear@4 80 Object *Scene::get_object(int idx) const
nuclear@4 81 {
nuclear@4 82 return objects[idx];
nuclear@4 83 }
nuclear@4 84
nuclear@4 85 void Scene::add_node(SceneNode *node)
nuclear@4 86 {
nuclear@4 87 nodes.push_back(node);
nuclear@4 88
nuclear@4 89 if(!node->get_parent()) {
nuclear@4 90 root->add_child(node);
nuclear@4 91 }
nuclear@4 92 }
nuclear@4 93
nuclear@4 94 int Scene::get_node_count() const
nuclear@4 95 {
nuclear@4 96 return (int)nodes.size();
nuclear@4 97 }
nuclear@4 98
nuclear@4 99 SceneNode *Scene::get_node(int idx) const
nuclear@4 100 {
nuclear@4 101 return nodes[idx];
nuclear@4 102 }
nuclear@4 103
nuclear@4 104 void Scene::use_camera(Camera *cam)
nuclear@4 105 {
nuclear@4 106 active_cam = cam;
nuclear@4 107 }
nuclear@4 108
nuclear@4 109 Camera *Scene::get_active_camera() const
nuclear@4 110 {
nuclear@4 111 return active_cam;
nuclear@4 112 }
nuclear@4 113
nuclear@43 114 std::list<ObjectInstance> Scene::gen_light_list() const
nuclear@43 115 {
nuclear@43 116 std::list<ObjectInstance> list;
nuclear@43 117
nuclear@43 118 for(auto n : nodes) {
nuclear@43 119 int nobj = n->get_num_objects();
nuclear@43 120 for(int i=0; i<nobj; i++) {
nuclear@43 121 Object *o = n->get_object(i);
nuclear@43 122 if(o->get_type() != ObjType::geom) {
nuclear@43 123 continue;
nuclear@43 124 }
nuclear@43 125 GeomObject *go = (GeomObject*)o;
nuclear@43 126
nuclear@43 127 if(go->mtl.get_attrib_value("emissive") > 0.0) {
nuclear@43 128 ObjectInstance inst;
nuclear@43 129 inst.obj = go;
nuclear@43 130 inst.node = n;
nuclear@43 131
nuclear@43 132 list.push_back(inst);
nuclear@43 133 }
nuclear@43 134 }
nuclear@43 135 }
nuclear@43 136 return std::move(list);
nuclear@43 137 }
nuclear@43 138
nuclear@4 139 void Scene::update(long msec)
nuclear@4 140 {
nuclear@4 141 root->update(msec);
nuclear@4 142 }
nuclear@4 143
nuclear@4 144 bool Scene::intersect(const Ray &ray, RayHit *hit) const
nuclear@4 145 {
nuclear@4 146 return root->intersect(ray, hit);
nuclear@4 147 }
nuclear@4 148
nuclear@4 149 bool Scene::load(const char *fname)
nuclear@4 150 {
nuclear@19 151 FILE *fp = fopen(fname, "r");
nuclear@19 152 if(!fp) {
nuclear@19 153 fprintf(stderr, "failed to open scene file: %s\n", fname);
nuclear@19 154 return false;
nuclear@19 155 }
nuclear@19 156
nuclear@19 157 bool res = load(fp);
nuclear@19 158 fclose(fp);
nuclear@19 159 return res;
nuclear@4 160 }
nuclear@19 161
nuclear@19 162 static char *strip_wspace(char *str)
nuclear@19 163 {
nuclear@19 164 while(*str && isspace(*str)) str++;
nuclear@23 165
nuclear@23 166 char *comment = strchr(str, '#');
nuclear@23 167 if(comment) *comment = 0;
nuclear@23 168
nuclear@23 169 if(str) {
nuclear@23 170 char *end = str + strlen(str) - 1;
nuclear@23 171 while(end > str && isspace(*end)) end--;
nuclear@23 172 end[1] = 0;
nuclear@23 173 }
nuclear@23 174
nuclear@19 175 return str;
nuclear@19 176 }
nuclear@19 177
nuclear@19 178 bool Scene::load(FILE *fp)
nuclear@19 179 {
nuclear@28 180 static char buf[4096];
nuclear@28 181 int offset = 0;
nuclear@19 182
nuclear@28 183 while(fgets(buf + offset, sizeof buf - offset, fp)) {
nuclear@28 184 // handle line continuations
nuclear@28 185 int len = strlen(buf);
nuclear@28 186 if(strcmp(buf + len - 2, "\\\n") == 0 || strcmp(buf + len - 2, "\\\r") == 0) {
nuclear@28 187 offset = len - 2;
nuclear@28 188 continue;
nuclear@28 189 }
nuclear@28 190 if(strcmp(buf + len - 3, "\\\r\n") == 0) {
nuclear@28 191 offset = len - 3;
nuclear@28 192 continue;
nuclear@28 193 }
nuclear@28 194 offset = 0;
nuclear@28 195
nuclear@19 196 char *line = strip_wspace(buf);
nuclear@19 197 if(!line || !*line || *line == '#') {
nuclear@19 198 continue;
nuclear@19 199 }
nuclear@19 200
nuclear@19 201 std::vector<char*> cmd;
nuclear@19 202
nuclear@19 203 char *tok = 0;
nuclear@19 204 while((tok = strtok(tok ? 0 : line, " \t\v\n\r"))) {
nuclear@19 205 cmd.push_back(tok);
nuclear@19 206 }
nuclear@19 207 cmd.push_back(0);
nuclear@19 208
nuclear@19 209
nuclear@19 210 if(!proc_cmd((int)cmd.size() - 1, &cmd[0])) {
nuclear@19 211 return false;
nuclear@19 212 }
nuclear@19 213 }
nuclear@19 214
nuclear@19 215 return true;
nuclear@19 216 }
nuclear@19 217
nuclear@19 218 static int parse_vec(char **argv, float *vec)
nuclear@19 219 {
nuclear@19 220 int nelem = 0;
nuclear@19 221 char *endp;
nuclear@19 222
nuclear@19 223 for(int i=0; i<4; i++) {
nuclear@19 224 if(!argv[i]) break;
nuclear@19 225
nuclear@19 226 vec[nelem] = strtod(argv[i], &endp);
nuclear@19 227 if(endp == argv[i]) {
nuclear@19 228 break;
nuclear@19 229 }
nuclear@19 230 nelem++;
nuclear@19 231 }
nuclear@19 232 return nelem;
nuclear@19 233 }
nuclear@19 234
nuclear@22 235 static int parse_brdf(char **argv, Reflectance **brdf_ptr)
nuclear@22 236 {
nuclear@22 237 CompositeRefl *composite = 0;
nuclear@22 238 Reflectance *brdf = 0;
nuclear@22 239 float weight = 1.0;
nuclear@22 240 int i;
nuclear@22 241
nuclear@22 242 for(i=0; argv[i]; i++) {
nuclear@22 243 if(strcmp(argv[i], "-brdf") == 0) {
nuclear@22 244 if(strcmp(argv[++i], "lambert") == 0) {
nuclear@22 245 if(composite && brdf) {
nuclear@22 246 composite->add_brdf(brdf, weight);
nuclear@22 247 }
nuclear@22 248 brdf = new LambertRefl;
nuclear@22 249 if(!composite) break;
nuclear@22 250
nuclear@28 251 } else if(strcmp(argv[i], "phong") == 0) {
nuclear@28 252 if(composite && brdf) {
nuclear@28 253 composite->add_brdf(brdf, weight);
nuclear@28 254 }
nuclear@28 255 brdf = new PhongRefl;
nuclear@28 256 if(!composite) break;
nuclear@28 257
nuclear@22 258 } else if(strcmp(argv[i], "mirror") == 0) {
nuclear@22 259 if(composite && brdf) {
nuclear@22 260 composite->add_brdf(brdf, weight);
nuclear@22 261 }
nuclear@22 262 brdf = new MirrorRefl;
nuclear@22 263 if(!composite) break;
nuclear@22 264
nuclear@22 265 } else if(strcmp(argv[i], "composite") == 0) {
nuclear@22 266 if(composite) {
nuclear@22 267 fprintf(stderr, "can't have nested composite BRDFs\n");
nuclear@22 268 return -1;
nuclear@22 269 }
nuclear@22 270 composite = new CompositeRefl;
nuclear@22 271
nuclear@22 272 } else {
nuclear@22 273 --i;
nuclear@22 274 break;
nuclear@22 275 }
nuclear@22 276
nuclear@22 277 } else if(strcmp(argv[i], "-brdf-weight") == 0) {
nuclear@22 278 if(!composite) {
nuclear@22 279 fprintf(stderr, "-brdf-weight is invalid without a composite brdf\n");
nuclear@22 280 return -1;
nuclear@22 281 }
nuclear@22 282 char *endp;
nuclear@22 283 weight = strtod(argv[++i], &endp);
nuclear@22 284 if(endp == argv[i]) {
nuclear@22 285 fprintf(stderr, "-brdf-weight must be followed by a number\n");
nuclear@22 286 return -1;
nuclear@22 287 }
nuclear@22 288 } else {
nuclear@22 289 --i;
nuclear@22 290 break;
nuclear@22 291 }
nuclear@22 292 }
nuclear@22 293
nuclear@22 294 if(composite && brdf) {
nuclear@22 295 composite->add_brdf(brdf, weight);
nuclear@22 296 *brdf_ptr = composite;
nuclear@22 297 } else {
nuclear@22 298 *brdf_ptr = brdf;
nuclear@22 299 }
nuclear@22 300 return i;
nuclear@22 301 }
nuclear@22 302
nuclear@19 303 static SceneNode *proc_object(Object *obj, int argc, char **argv)
nuclear@19 304 {
nuclear@19 305 float vec[4];
nuclear@19 306 SceneNode *node = new SceneNode(obj);
nuclear@19 307 GeomObject *gobj = (GeomObject*)obj;
nuclear@19 308
nuclear@19 309 for(int i=1; i<argc; i++) {
nuclear@19 310 if(strcmp(argv[i], "-position") == 0) {
nuclear@19 311 int nelem = parse_vec(argv + i + 1, vec);
nuclear@19 312 if(nelem < 3) {
nuclear@19 313 fprintf(stderr, "-position must be followed by 3 numbers\n");
nuclear@19 314 goto err;
nuclear@19 315 }
nuclear@19 316 node->set_position(Vector3(vec[0], vec[1], vec[2]));
nuclear@19 317 i += 3;
nuclear@19 318
nuclear@19 319 } else if(strcmp(argv[i], "-rotation") == 0) {
nuclear@19 320 int nelem = parse_vec(argv + i + 1, vec);
nuclear@19 321 if(nelem < 4) {
nuclear@19 322 fprintf(stderr, "-rotation must be followed by axis vector and angle\n");
nuclear@19 323 goto err;
nuclear@19 324 }
nuclear@19 325 node->set_rotation(Quaternion(Vector3(vec[0], vec[1], vec[2]), vec[3]));
nuclear@19 326 i += 4;
nuclear@19 327
nuclear@19 328 } else if(strcmp(argv[i], "-scaling") == 0) {
nuclear@19 329 int nelem = parse_vec(argv + i + 1, vec);
nuclear@19 330 Vector3 s;
nuclear@19 331 if(nelem == 1) {
nuclear@19 332 s.x = s.y = s.z = vec[0];
nuclear@19 333 } else if(nelem == 3) {
nuclear@19 334 s = Vector3(vec[0], vec[1], vec[2]);
nuclear@19 335 } else {
nuclear@19 336 fprintf(stderr, "-scaling must be followed by 1 or 3 numbers\n");
nuclear@19 337 goto err;
nuclear@19 338 }
nuclear@19 339 node->set_scaling(s);
nuclear@19 340 i += nelem;
nuclear@19 341
nuclear@19 342 } else if(strcmp(argv[i], "-diffuse") == 0) {
nuclear@19 343 int nelem = parse_vec(argv + i + 1, vec);
nuclear@19 344 if(nelem < 3) {
nuclear@19 345 fprintf(stderr, "-diffuse must be followed by a color (r g b)\n");
nuclear@19 346 goto err;
nuclear@19 347 }
nuclear@19 348 gobj->mtl.set_attrib("diffuse", Color(vec[0], vec[1], vec[2]));
nuclear@19 349 i += 3;
nuclear@19 350
nuclear@19 351 } else if(strcmp(argv[i], "-specular") == 0) {
nuclear@19 352 int nelem = parse_vec(argv + i + 1, vec);
nuclear@19 353 if(nelem < 3) {
nuclear@19 354 fprintf(stderr, "-specular must be followed by a color\n");
nuclear@19 355 goto err;
nuclear@19 356 }
nuclear@19 357 gobj->mtl.set_attrib("specular", Color(vec[0], vec[1], vec[2]));
nuclear@19 358 i += 3;
nuclear@19 359
nuclear@19 360 } else if(strcmp(argv[i], "-emissive") == 0) {
nuclear@19 361 int nelem = parse_vec(argv + i + 1, vec);
nuclear@19 362 Color emissive;
nuclear@19 363 if(nelem == 1) {
nuclear@19 364 emissive = Vector3(1, 1, 1);
nuclear@19 365 } else if(nelem == 3) {
nuclear@19 366 emissive = Vector3(vec[0], vec[1], vec[2]);
nuclear@19 367 } else {
nuclear@19 368 fprintf(stderr, "-emissive must be followed by an intensity or a color\n");
nuclear@19 369 goto err;
nuclear@19 370 }
nuclear@19 371 i += nelem;
nuclear@19 372 gobj->mtl.set_attrib("emissive", emissive);
nuclear@19 373
nuclear@22 374 } else if(strcmp(argv[i], "-brdf") == 0) {
nuclear@22 375 int nelem = parse_brdf(argv + i, &gobj->brdf);
nuclear@22 376 if(nelem == -1) {
nuclear@22 377 goto err;
nuclear@22 378 }
nuclear@22 379 i += nelem;
nuclear@22 380
nuclear@19 381 } else {
nuclear@19 382 fprintf(stderr, "unrecognized %s option: %s\n", argv[0], argv[i]);
nuclear@19 383 goto err;
nuclear@19 384 }
nuclear@19 385 }
nuclear@19 386
nuclear@19 387 return node;
nuclear@19 388 err:
nuclear@19 389 delete node;
nuclear@19 390 return 0;
nuclear@19 391 }
nuclear@19 392
nuclear@19 393 static Camera *proc_camera(int argc, char **argv)
nuclear@19 394 {
nuclear@19 395 float vec[4];
nuclear@19 396 TargetCamera *cam = new TargetCamera;
nuclear@19 397
nuclear@19 398 for(int i=1; i<argc; i++) {
nuclear@19 399 if(strcmp(argv[i], "-position") == 0) {
nuclear@19 400 int nelem = parse_vec(argv + i + 1, vec);
nuclear@19 401 if(nelem < 3) {
nuclear@19 402 fprintf(stderr, "-position must be followed by 3 numbers\n");
nuclear@19 403 goto err;
nuclear@19 404 }
nuclear@19 405 cam->set_position(Vector3(vec[0], vec[1], vec[2]));
nuclear@19 406 i += 3;
nuclear@19 407
nuclear@19 408 } else if(strcmp(argv[i], "-target") == 0) {
nuclear@19 409 int nelem = parse_vec(argv + i + 1, vec);
nuclear@19 410 if(nelem < 3) {
nuclear@19 411 fprintf(stderr, "-target must be followed by 3 numbers\n");
nuclear@19 412 goto err;
nuclear@19 413 }
nuclear@19 414 cam->set_target(Vector3(vec[0], vec[1], vec[2]));
nuclear@19 415 i += 3;
nuclear@19 416
nuclear@19 417 } else if(strcmp(argv[i], "-fov") == 0) {
nuclear@19 418 int nelem = parse_vec(argv + i + 1, vec);
nuclear@19 419 if(nelem < 1) {
nuclear@19 420 fprintf(stderr, "-fov must be followed by the field of view in degrees\n");
nuclear@19 421 goto err;
nuclear@19 422 }
nuclear@19 423 cam->set_fov(vec[0]);
nuclear@19 424 i += 1;
nuclear@19 425
nuclear@19 426 } else {
nuclear@19 427 fprintf(stderr, "unrecognized camera option: %s\n", argv[i]);
nuclear@19 428 goto err;
nuclear@19 429 }
nuclear@19 430 }
nuclear@19 431 return cam;
nuclear@19 432
nuclear@19 433 err:
nuclear@19 434 delete cam;
nuclear@19 435 return 0;
nuclear@19 436 }
nuclear@19 437
nuclear@19 438 bool Scene::proc_cmd(int argc, char **argv)
nuclear@19 439 {
nuclear@19 440 Object *obj = 0;
nuclear@19 441 SceneNode *node = 0;
nuclear@19 442
nuclear@19 443 if(strcmp(argv[0], "sphere") == 0) {
nuclear@19 444 obj = new Sphere;
nuclear@19 445 node = proc_object(obj, argc, argv);
nuclear@19 446
nuclear@19 447 } else if(strcmp(argv[0], "box") == 0) {
nuclear@19 448 obj = new Box;
nuclear@19 449 node = proc_object(obj, argc, argv);
nuclear@19 450
nuclear@19 451 } else if(strcmp(argv[0], "mesh") == 0) {
nuclear@19 452 fprintf(stderr, "meshes not implemented yet\n");
nuclear@19 453 return false;
nuclear@19 454
nuclear@19 455 } else if(strcmp(argv[0], "camera") == 0) {
nuclear@19 456 Camera *cam = proc_camera(argc, argv);
nuclear@19 457 if(!cam) {
nuclear@19 458 return false;
nuclear@19 459 }
nuclear@19 460 use_camera(cam);
nuclear@19 461
nuclear@19 462 } else {
nuclear@19 463 fprintf(stderr, "unknown command: %s\n", argv[0]);
nuclear@19 464 return false;
nuclear@19 465 }
nuclear@19 466
nuclear@19 467 if(obj) {
nuclear@19 468 if(!node) {
nuclear@19 469 return false;
nuclear@19 470 }
nuclear@19 471 add_object(obj);
nuclear@19 472 add_node(node);
nuclear@19 473 }
nuclear@19 474
nuclear@19 475 return true;
nuclear@19 476 }