nuclear@19: #include nuclear@19: #include nuclear@19: #include nuclear@4: #include "scene.h" nuclear@19: #include "snode.h" nuclear@19: #include "object.h" nuclear@19: #include "geomobj.h" nuclear@4: nuclear@8: // default enviromental parameters nuclear@8: Environment::Environment() nuclear@19: : bgcolor(0.01, 0.01, 0.01), ambient(0.01, 0.01, 0.01) nuclear@8: { nuclear@8: } nuclear@8: nuclear@4: Scene::Scene() nuclear@4: { nuclear@4: active_cam = 0; nuclear@4: root = new SceneNode; nuclear@4: } nuclear@4: nuclear@4: Scene::~Scene() nuclear@4: { nuclear@40: clear(); nuclear@40: } nuclear@40: nuclear@40: void Scene::clear() nuclear@40: { nuclear@4: for(auto obj : objects) { nuclear@4: delete obj; nuclear@4: } nuclear@40: objects.clear(); nuclear@40: nuclear@4: for(auto node : nodes) { nuclear@4: delete node; nuclear@4: } nuclear@40: nodes.clear(); nuclear@40: nuclear@4: delete root; nuclear@40: root = 0; nuclear@40: nuclear@40: active_cam = 0; nuclear@4: } nuclear@4: nuclear@8: void Scene::set_env(const Environment &env) nuclear@8: { nuclear@8: this->env = env; nuclear@8: } nuclear@8: nuclear@8: Environment &Scene::get_env() nuclear@8: { nuclear@8: return env; nuclear@8: } nuclear@8: nuclear@8: const Environment &Scene::get_env() const nuclear@8: { nuclear@8: return env; nuclear@8: } nuclear@8: nuclear@8: Color Scene::get_env_color() const nuclear@8: { nuclear@8: return env.bgcolor; nuclear@8: } nuclear@8: nuclear@8: Color Scene::get_env_color(const Ray &ray) const nuclear@8: { nuclear@8: // TODO nuclear@8: return get_env_color(); nuclear@8: } nuclear@8: nuclear@4: void Scene::add_object(Object *obj) nuclear@4: { nuclear@4: objects.push_back(obj); nuclear@4: } nuclear@4: nuclear@4: int Scene::get_object_count() const nuclear@4: { nuclear@4: return (int)objects.size(); nuclear@4: } nuclear@4: nuclear@4: Object *Scene::get_object(int idx) const nuclear@4: { nuclear@4: return objects[idx]; nuclear@4: } nuclear@4: nuclear@4: void Scene::add_node(SceneNode *node) nuclear@4: { nuclear@4: nodes.push_back(node); nuclear@4: nuclear@4: if(!node->get_parent()) { nuclear@4: root->add_child(node); nuclear@4: } nuclear@4: } nuclear@4: nuclear@4: int Scene::get_node_count() const nuclear@4: { nuclear@4: return (int)nodes.size(); nuclear@4: } nuclear@4: nuclear@4: SceneNode *Scene::get_node(int idx) const nuclear@4: { nuclear@4: return nodes[idx]; nuclear@4: } nuclear@4: nuclear@4: void Scene::use_camera(Camera *cam) nuclear@4: { nuclear@4: active_cam = cam; nuclear@4: } nuclear@4: nuclear@4: Camera *Scene::get_active_camera() const nuclear@4: { nuclear@4: return active_cam; nuclear@4: } nuclear@4: nuclear@43: std::list Scene::gen_light_list() const nuclear@43: { nuclear@43: std::list list; nuclear@43: nuclear@43: for(auto n : nodes) { nuclear@43: int nobj = n->get_num_objects(); nuclear@43: for(int i=0; iget_object(i); nuclear@43: if(o->get_type() != ObjType::geom) { nuclear@43: continue; nuclear@43: } nuclear@43: GeomObject *go = (GeomObject*)o; nuclear@43: nuclear@43: if(go->mtl.get_attrib_value("emissive") > 0.0) { nuclear@43: ObjectInstance inst; nuclear@43: inst.obj = go; nuclear@43: inst.node = n; nuclear@43: nuclear@43: list.push_back(inst); nuclear@43: } nuclear@43: } nuclear@43: } nuclear@43: return std::move(list); nuclear@43: } nuclear@43: nuclear@4: void Scene::update(long msec) nuclear@4: { nuclear@4: root->update(msec); nuclear@4: } nuclear@4: nuclear@4: bool Scene::intersect(const Ray &ray, RayHit *hit) const nuclear@4: { nuclear@4: return root->intersect(ray, hit); nuclear@4: } nuclear@4: nuclear@4: bool Scene::load(const char *fname) nuclear@4: { nuclear@19: FILE *fp = fopen(fname, "r"); nuclear@19: if(!fp) { nuclear@19: fprintf(stderr, "failed to open scene file: %s\n", fname); nuclear@19: return false; nuclear@19: } nuclear@19: nuclear@19: bool res = load(fp); nuclear@19: fclose(fp); nuclear@19: return res; nuclear@4: } nuclear@19: nuclear@19: static char *strip_wspace(char *str) nuclear@19: { nuclear@19: while(*str && isspace(*str)) str++; nuclear@23: nuclear@23: char *comment = strchr(str, '#'); nuclear@23: if(comment) *comment = 0; nuclear@23: nuclear@23: if(str) { nuclear@23: char *end = str + strlen(str) - 1; nuclear@23: while(end > str && isspace(*end)) end--; nuclear@23: end[1] = 0; nuclear@23: } nuclear@23: nuclear@19: return str; nuclear@19: } nuclear@19: nuclear@19: bool Scene::load(FILE *fp) nuclear@19: { nuclear@28: static char buf[4096]; nuclear@28: int offset = 0; nuclear@19: nuclear@28: while(fgets(buf + offset, sizeof buf - offset, fp)) { nuclear@28: // handle line continuations nuclear@28: int len = strlen(buf); nuclear@28: if(strcmp(buf + len - 2, "\\\n") == 0 || strcmp(buf + len - 2, "\\\r") == 0) { nuclear@28: offset = len - 2; nuclear@28: continue; nuclear@28: } nuclear@28: if(strcmp(buf + len - 3, "\\\r\n") == 0) { nuclear@28: offset = len - 3; nuclear@28: continue; nuclear@28: } nuclear@28: offset = 0; nuclear@28: nuclear@19: char *line = strip_wspace(buf); nuclear@19: if(!line || !*line || *line == '#') { nuclear@19: continue; nuclear@19: } nuclear@19: nuclear@19: std::vector cmd; nuclear@19: nuclear@19: char *tok = 0; nuclear@19: while((tok = strtok(tok ? 0 : line, " \t\v\n\r"))) { nuclear@19: cmd.push_back(tok); nuclear@19: } nuclear@19: cmd.push_back(0); nuclear@19: nuclear@19: nuclear@19: if(!proc_cmd((int)cmd.size() - 1, &cmd[0])) { nuclear@19: return false; nuclear@19: } nuclear@19: } nuclear@19: nuclear@19: return true; nuclear@19: } nuclear@19: nuclear@19: static int parse_vec(char **argv, float *vec) nuclear@19: { nuclear@19: int nelem = 0; nuclear@19: char *endp; nuclear@19: nuclear@19: for(int i=0; i<4; i++) { nuclear@19: if(!argv[i]) break; nuclear@19: nuclear@19: vec[nelem] = strtod(argv[i], &endp); nuclear@19: if(endp == argv[i]) { nuclear@19: break; nuclear@19: } nuclear@19: nelem++; nuclear@19: } nuclear@19: return nelem; nuclear@19: } nuclear@19: nuclear@22: static int parse_brdf(char **argv, Reflectance **brdf_ptr) nuclear@22: { nuclear@22: CompositeRefl *composite = 0; nuclear@22: Reflectance *brdf = 0; nuclear@22: float weight = 1.0; nuclear@22: int i; nuclear@22: nuclear@22: for(i=0; argv[i]; i++) { nuclear@22: if(strcmp(argv[i], "-brdf") == 0) { nuclear@22: if(strcmp(argv[++i], "lambert") == 0) { nuclear@22: if(composite && brdf) { nuclear@22: composite->add_brdf(brdf, weight); nuclear@22: } nuclear@22: brdf = new LambertRefl; nuclear@22: if(!composite) break; nuclear@22: nuclear@28: } else if(strcmp(argv[i], "phong") == 0) { nuclear@28: if(composite && brdf) { nuclear@28: composite->add_brdf(brdf, weight); nuclear@28: } nuclear@28: brdf = new PhongRefl; nuclear@28: if(!composite) break; nuclear@28: nuclear@22: } else if(strcmp(argv[i], "mirror") == 0) { nuclear@22: if(composite && brdf) { nuclear@22: composite->add_brdf(brdf, weight); nuclear@22: } nuclear@22: brdf = new MirrorRefl; nuclear@22: if(!composite) break; nuclear@22: nuclear@22: } else if(strcmp(argv[i], "composite") == 0) { nuclear@22: if(composite) { nuclear@22: fprintf(stderr, "can't have nested composite BRDFs\n"); nuclear@22: return -1; nuclear@22: } nuclear@22: composite = new CompositeRefl; nuclear@22: nuclear@22: } else { nuclear@22: --i; nuclear@22: break; nuclear@22: } nuclear@22: nuclear@22: } else if(strcmp(argv[i], "-brdf-weight") == 0) { nuclear@22: if(!composite) { nuclear@22: fprintf(stderr, "-brdf-weight is invalid without a composite brdf\n"); nuclear@22: return -1; nuclear@22: } nuclear@22: char *endp; nuclear@22: weight = strtod(argv[++i], &endp); nuclear@22: if(endp == argv[i]) { nuclear@22: fprintf(stderr, "-brdf-weight must be followed by a number\n"); nuclear@22: return -1; nuclear@22: } nuclear@22: } else { nuclear@22: --i; nuclear@22: break; nuclear@22: } nuclear@22: } nuclear@22: nuclear@22: if(composite && brdf) { nuclear@22: composite->add_brdf(brdf, weight); nuclear@22: *brdf_ptr = composite; nuclear@22: } else { nuclear@22: *brdf_ptr = brdf; nuclear@22: } nuclear@22: return i; nuclear@22: } nuclear@22: nuclear@19: static SceneNode *proc_object(Object *obj, int argc, char **argv) nuclear@19: { nuclear@19: float vec[4]; nuclear@19: SceneNode *node = new SceneNode(obj); nuclear@19: GeomObject *gobj = (GeomObject*)obj; nuclear@19: nuclear@19: for(int i=1; iset_position(Vector3(vec[0], vec[1], vec[2])); nuclear@19: i += 3; nuclear@19: nuclear@19: } else if(strcmp(argv[i], "-rotation") == 0) { nuclear@19: int nelem = parse_vec(argv + i + 1, vec); nuclear@19: if(nelem < 4) { nuclear@19: fprintf(stderr, "-rotation must be followed by axis vector and angle\n"); nuclear@19: goto err; nuclear@19: } nuclear@19: node->set_rotation(Quaternion(Vector3(vec[0], vec[1], vec[2]), vec[3])); nuclear@19: i += 4; nuclear@19: nuclear@19: } else if(strcmp(argv[i], "-scaling") == 0) { nuclear@19: int nelem = parse_vec(argv + i + 1, vec); nuclear@19: Vector3 s; nuclear@19: if(nelem == 1) { nuclear@19: s.x = s.y = s.z = vec[0]; nuclear@19: } else if(nelem == 3) { nuclear@19: s = Vector3(vec[0], vec[1], vec[2]); nuclear@19: } else { nuclear@19: fprintf(stderr, "-scaling must be followed by 1 or 3 numbers\n"); nuclear@19: goto err; nuclear@19: } nuclear@19: node->set_scaling(s); nuclear@19: i += nelem; nuclear@19: nuclear@19: } else if(strcmp(argv[i], "-diffuse") == 0) { nuclear@19: int nelem = parse_vec(argv + i + 1, vec); nuclear@19: if(nelem < 3) { nuclear@19: fprintf(stderr, "-diffuse must be followed by a color (r g b)\n"); nuclear@19: goto err; nuclear@19: } nuclear@19: gobj->mtl.set_attrib("diffuse", Color(vec[0], vec[1], vec[2])); nuclear@19: i += 3; nuclear@19: nuclear@19: } else if(strcmp(argv[i], "-specular") == 0) { nuclear@19: int nelem = parse_vec(argv + i + 1, vec); nuclear@19: if(nelem < 3) { nuclear@19: fprintf(stderr, "-specular must be followed by a color\n"); nuclear@19: goto err; nuclear@19: } nuclear@19: gobj->mtl.set_attrib("specular", Color(vec[0], vec[1], vec[2])); nuclear@19: i += 3; nuclear@19: nuclear@19: } else if(strcmp(argv[i], "-emissive") == 0) { nuclear@19: int nelem = parse_vec(argv + i + 1, vec); nuclear@19: Color emissive; nuclear@19: if(nelem == 1) { nuclear@19: emissive = Vector3(1, 1, 1); nuclear@19: } else if(nelem == 3) { nuclear@19: emissive = Vector3(vec[0], vec[1], vec[2]); nuclear@19: } else { nuclear@19: fprintf(stderr, "-emissive must be followed by an intensity or a color\n"); nuclear@19: goto err; nuclear@19: } nuclear@19: i += nelem; nuclear@19: gobj->mtl.set_attrib("emissive", emissive); nuclear@19: nuclear@22: } else if(strcmp(argv[i], "-brdf") == 0) { nuclear@22: int nelem = parse_brdf(argv + i, &gobj->brdf); nuclear@22: if(nelem == -1) { nuclear@22: goto err; nuclear@22: } nuclear@22: i += nelem; nuclear@22: nuclear@19: } else { nuclear@19: fprintf(stderr, "unrecognized %s option: %s\n", argv[0], argv[i]); nuclear@19: goto err; nuclear@19: } nuclear@19: } nuclear@19: nuclear@19: return node; nuclear@19: err: nuclear@19: delete node; nuclear@19: return 0; nuclear@19: } nuclear@19: nuclear@19: static Camera *proc_camera(int argc, char **argv) nuclear@19: { nuclear@19: float vec[4]; nuclear@19: TargetCamera *cam = new TargetCamera; nuclear@19: nuclear@19: for(int i=1; iset_position(Vector3(vec[0], vec[1], vec[2])); nuclear@19: i += 3; nuclear@19: nuclear@19: } else if(strcmp(argv[i], "-target") == 0) { nuclear@19: int nelem = parse_vec(argv + i + 1, vec); nuclear@19: if(nelem < 3) { nuclear@19: fprintf(stderr, "-target must be followed by 3 numbers\n"); nuclear@19: goto err; nuclear@19: } nuclear@19: cam->set_target(Vector3(vec[0], vec[1], vec[2])); nuclear@19: i += 3; nuclear@19: nuclear@19: } else if(strcmp(argv[i], "-fov") == 0) { nuclear@19: int nelem = parse_vec(argv + i + 1, vec); nuclear@19: if(nelem < 1) { nuclear@19: fprintf(stderr, "-fov must be followed by the field of view in degrees\n"); nuclear@19: goto err; nuclear@19: } nuclear@19: cam->set_fov(vec[0]); nuclear@19: i += 1; nuclear@19: nuclear@19: } else { nuclear@19: fprintf(stderr, "unrecognized camera option: %s\n", argv[i]); nuclear@19: goto err; nuclear@19: } nuclear@19: } nuclear@19: return cam; nuclear@19: nuclear@19: err: nuclear@19: delete cam; nuclear@19: return 0; nuclear@19: } nuclear@19: nuclear@19: bool Scene::proc_cmd(int argc, char **argv) nuclear@19: { nuclear@19: Object *obj = 0; nuclear@19: SceneNode *node = 0; nuclear@19: nuclear@19: if(strcmp(argv[0], "sphere") == 0) { nuclear@19: obj = new Sphere; nuclear@19: node = proc_object(obj, argc, argv); nuclear@19: nuclear@19: } else if(strcmp(argv[0], "box") == 0) { nuclear@19: obj = new Box; nuclear@19: node = proc_object(obj, argc, argv); nuclear@19: nuclear@19: } else if(strcmp(argv[0], "mesh") == 0) { nuclear@19: fprintf(stderr, "meshes not implemented yet\n"); nuclear@19: return false; nuclear@19: nuclear@19: } else if(strcmp(argv[0], "camera") == 0) { nuclear@19: Camera *cam = proc_camera(argc, argv); nuclear@19: if(!cam) { nuclear@19: return false; nuclear@19: } nuclear@19: use_camera(cam); nuclear@19: nuclear@19: } else { nuclear@19: fprintf(stderr, "unknown command: %s\n", argv[0]); nuclear@19: return false; nuclear@19: } nuclear@19: nuclear@19: if(obj) { nuclear@19: if(!node) { nuclear@19: return false; nuclear@19: } nuclear@19: add_object(obj); nuclear@19: add_node(node); nuclear@19: } nuclear@19: nuclear@19: return true; nuclear@19: }