nuclear@0: #include nuclear@0: #include nuclear@0: #include "gpuscene.h" nuclear@0: #include "sphere.h" nuclear@0: #include "plane.h" nuclear@0: #include "box.h" nuclear@0: #include "opengl.h" nuclear@0: nuclear@0: GPUScene::GPUScene() nuclear@0: { nuclear@0: glGenTextures(NUM_TEXTURES, textures); nuclear@0: nuclear@0: for(int i=0; i spheres; nuclear@0: std::vector planes; nuclear@0: std::vector boxes; nuclear@4: std::vector cones; nuclear@0: nuclear@0: // collect all objects into the different type-specific arrays nuclear@0: for(auto obj : objects) { nuclear@0: if((sph = dynamic_cast(obj))) { nuclear@0: spheres.push_back(sph); nuclear@0: nuclear@0: } else if((plane = dynamic_cast(obj))) { nuclear@0: planes.push_back(plane); nuclear@0: nuclear@0: } else if((box = dynamic_cast(obj))) { nuclear@0: boxes.push_back(box); nuclear@0: nuclear@4: } else if((cone = dynamic_cast(obj))) { nuclear@4: cones.push_back(cone); nuclear@4: nuclear@0: } else { nuclear@0: fprintf(stderr, "skipping object of unknown type: %s\n", obj->get_name()); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: create_sphere_texture(spheres); nuclear@0: create_plane_texture(planes); nuclear@0: create_box_texture(boxes); nuclear@4: create_cone_texture(cones); nuclear@0: nuclear@0: create_env_texture(); nuclear@0: create_xform_texture(); nuclear@0: nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: unsigned int GPUScene::get_texture(int which) const nuclear@0: { nuclear@0: return textures[which]; nuclear@0: } nuclear@0: nuclear@0: #define MAT_START 4 nuclear@0: static void copy_material(Vector4 *ptr, const Material *mtl) nuclear@0: { nuclear@0: ptr[MAT_START].x = mtl->diffuse.x; nuclear@0: ptr[MAT_START].y = mtl->diffuse.y; nuclear@0: ptr[MAT_START].z = mtl->diffuse.z; nuclear@0: ptr[MAT_START].w = mtl->transparency; nuclear@0: nuclear@0: ptr[MAT_START + 1].x = mtl->specular.x; nuclear@0: ptr[MAT_START + 1].y = mtl->specular.y; nuclear@0: ptr[MAT_START + 1].z = mtl->specular.z; nuclear@0: ptr[MAT_START + 1].w = mtl->shininess; nuclear@0: nuclear@0: ptr[MAT_START + 2].x = mtl->reflectivity; nuclear@0: ptr[MAT_START + 2].y = mtl->ior; nuclear@0: nuclear@0: ptr[MAT_START + 3] = mtl->mega_rect; nuclear@0: } nuclear@0: nuclear@0: int GPUScene::object_index(const Object *obj) const nuclear@0: { nuclear@0: for(int i=0; i<(int)objects.size(); i++) { nuclear@0: if(objects[i] == obj) { nuclear@0: return i; nuclear@0: } nuclear@0: } nuclear@0: abort(); // can't happen nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: #define OBJ_LINE_WIDTH 16 nuclear@0: void GPUScene::create_sphere_texture(const std::vector &spheres) nuclear@0: { nuclear@0: int xsz = OBJ_LINE_WIDTH; nuclear@0: int ysz = (int)spheres.size() + 1; nuclear@0: int tex_ysz = next_pow2(ysz); nuclear@0: nuclear@0: Vector4 *pixels = new Vector4[xsz * tex_ysz]; nuclear@0: nuclear@0: pixels[0].x = (float)ysz; nuclear@0: pixels[0].y = (float)tex_ysz; nuclear@0: pixels[0].z = 0.5; nuclear@0: pixels[0].w = 1.0; nuclear@0: nuclear@0: Vector4 *pixptr = pixels + xsz; nuclear@0: nuclear@0: for(size_t i=0; ipos.x; nuclear@0: pixptr[1].y = spheres[i]->pos.y; nuclear@0: pixptr[1].z = spheres[i]->pos.z; nuclear@0: pixptr[1].w = spheres[i]->radius; nuclear@0: nuclear@0: copy_material(pixptr, &spheres[i]->material); nuclear@0: pixptr += OBJ_LINE_WIDTH; nuclear@0: } nuclear@0: nuclear@0: glBindTexture(GL_TEXTURE_2D, textures[TEX_SPHERE]); nuclear@0: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, xsz, tex_ysz, 0, GL_RGBA, GL_FLOAT, pixels); nuclear@0: nuclear@0: delete [] pixels; nuclear@0: } nuclear@0: nuclear@0: void GPUScene::create_plane_texture(const std::vector &planes) nuclear@0: { nuclear@0: int xsz = OBJ_LINE_WIDTH; nuclear@0: int ysz = (int)planes.size() + 1; nuclear@0: int tex_ysz = next_pow2(ysz); nuclear@0: nuclear@0: Vector4 *pixels = new Vector4[xsz * tex_ysz]; nuclear@0: nuclear@0: pixels[0].x = (float)ysz; nuclear@0: pixels[0].y = (float)tex_ysz; nuclear@0: nuclear@0: Vector4 *pixptr = pixels + xsz; nuclear@0: nuclear@0: for(size_t i=0; inormal.x; nuclear@0: pixptr[1].y = planes[i]->normal.y; nuclear@0: pixptr[1].z = planes[i]->normal.z; nuclear@0: pixptr[1].w = planes[i]->dist; nuclear@0: nuclear@0: copy_material(pixptr, &planes[i]->material); nuclear@0: pixptr += OBJ_LINE_WIDTH; nuclear@0: } nuclear@0: nuclear@0: glBindTexture(GL_TEXTURE_2D, textures[TEX_PLANE]); nuclear@0: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, xsz, tex_ysz, 0, GL_RGBA, GL_FLOAT, pixels); nuclear@0: nuclear@0: delete [] pixels; nuclear@0: } nuclear@0: nuclear@0: void GPUScene::create_box_texture(const std::vector &boxes) nuclear@0: { nuclear@0: int xsz = OBJ_LINE_WIDTH; nuclear@0: int ysz = (int)boxes.size() + 1; nuclear@0: int tex_ysz = next_pow2(ysz); nuclear@0: nuclear@0: Vector4 *pixels = new Vector4[xsz * tex_ysz]; nuclear@0: nuclear@0: pixels[0].x = (float)ysz; nuclear@0: pixels[0].y = (float)tex_ysz; nuclear@0: nuclear@0: Vector4 *pixptr = pixels + xsz; nuclear@0: nuclear@0: for(size_t i=0; imin; nuclear@0: pixptr[2] = boxes[i]->max; nuclear@0: nuclear@0: copy_material(pixptr, &boxes[i]->material); nuclear@0: pixptr += OBJ_LINE_WIDTH; nuclear@0: } nuclear@0: nuclear@0: glBindTexture(GL_TEXTURE_2D, textures[TEX_BOX]); nuclear@0: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, xsz, tex_ysz, 0, GL_RGBA, GL_FLOAT, pixels); nuclear@0: nuclear@0: delete [] pixels; nuclear@0: } nuclear@0: nuclear@4: void GPUScene::create_cone_texture(const std::vector &cones) nuclear@4: { nuclear@4: int xsz = OBJ_LINE_WIDTH; nuclear@4: int ysz = (int)cones.size() + 1; nuclear@4: int tex_ysz = next_pow2(ysz); nuclear@4: nuclear@4: Vector4 *pixels = new Vector4[xsz * tex_ysz]; nuclear@4: nuclear@4: pixels[0].x = (float)ysz; nuclear@4: pixels[0].y = (float)tex_ysz; nuclear@4: nuclear@4: Vector4 *pixptr = pixels + xsz; nuclear@4: nuclear@4: for(size_t i=0; iangle; nuclear@4: pixptr[1].y = cones[i]->ymin; nuclear@4: pixptr[1].z = cones[i]->ymax; nuclear@4: pixptr[1].w = 0.0; nuclear@4: nuclear@4: copy_material(pixptr, &cones[i]->material); nuclear@4: pixptr += OBJ_LINE_WIDTH; nuclear@4: } nuclear@4: nuclear@4: glBindTexture(GL_TEXTURE_2D, textures[TEX_SPHERE]); nuclear@4: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, xsz, tex_ysz, 0, GL_RGBA, GL_FLOAT, pixels); nuclear@4: nuclear@4: delete [] pixels; nuclear@4: } nuclear@4: nuclear@4: nuclear@0: void GPUScene::create_megatexture() nuclear@0: { nuclear@0: // at least a 1x1 dummy white texture nuclear@0: int xsz = 1; nuclear@0: int ysz = 1; nuclear@0: int num_textures = 0; nuclear@0: nuclear@0: for(auto obj : objects) { nuclear@0: // only need 2D textures at this point nuclear@0: Texture *tex = dynamic_cast(obj->material.tex); nuclear@0: if(tex) { nuclear@0: const Image *img = tex->get_image(); nuclear@0: nuclear@0: xsz = std::max(xsz, img->xsz); nuclear@0: ysz += img->ysz; nuclear@0: num_textures++; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: int tex_xsz = next_pow2(xsz); nuclear@0: int tex_ysz = next_pow2(ysz); nuclear@0: nuclear@0: Color *pixels = new Color[tex_xsz * tex_ysz]; nuclear@0: nuclear@0: // null texture nuclear@0: pixels[0] = Color(1, 1, 1); nuclear@0: nuclear@0: Color *pixptr = pixels + tex_xsz; nuclear@0: nuclear@0: float offs_y = 0.0; nuclear@0: for(auto obj : objects) { nuclear@0: Texture *tex = dynamic_cast(obj->material.tex); nuclear@0: if(tex) { nuclear@0: const Image *img = tex->get_image(); nuclear@0: nuclear@0: Vector4 rect{0.0, offs_y, (float)img->xsz / (float)tex_xsz, nuclear@0: (float)img->ysz / (float)tex_ysz}; nuclear@0: nuclear@0: offs_y += rect.w; nuclear@0: nuclear@0: obj->material.mega_rect = rect; nuclear@0: nuclear@0: for(int i=0; iysz; i++) { nuclear@0: memcpy(pixptr, img->pixels + i * img->xsz, img->xsz * sizeof *pixels); nuclear@0: pixptr += tex_xsz; nuclear@0: } nuclear@0: } else { nuclear@0: obj->material.mega_rect = Vector4(0, 0, 0, 0); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: glBindTexture(GL_TEXTURE_2D, textures[TEX_TEXTURE]); nuclear@0: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, tex_xsz, tex_ysz, 0, GL_RGB, GL_FLOAT, pixels); nuclear@0: nuclear@0: delete [] pixels; nuclear@0: } nuclear@0: nuclear@0: void GPUScene::create_env_texture() nuclear@0: { nuclear@0: // create the scene cubemap, or a null cubemap if we don't have one nuclear@0: glDeleteTextures(1, textures + TEX_ENV); // cause it's not a 2D texture :) nuclear@0: nuclear@0: glGenTextures(1, textures + TEX_ENV); nuclear@0: glBindTexture(GL_TEXTURE_CUBE_MAP, textures[TEX_ENV]); nuclear@0: glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); nuclear@0: glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); nuclear@0: glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); nuclear@0: glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); nuclear@0: glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); nuclear@0: nuclear@0: if(envmap) { nuclear@0: // we have an environment cubemap, just pass the data to OpenGL nuclear@0: for(int i=0; i<6; i++) { nuclear@0: const Image *img = envmap->get_image(i); nuclear@0: nuclear@0: int face = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i; nuclear@0: glTexImage2D(face, 0, GL_RGB16F, img->xsz, img->ysz, 0, GL_RGB, GL_FLOAT, img->pixels); nuclear@0: } nuclear@0: } else { nuclear@0: // we don't have an env cubemap, make a dummy 1x1 cubemap with the background color nuclear@0: for(int i=0; i<6; i++) { nuclear@0: int face = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i; nuclear@0: glTexImage2D(face, 0, GL_RGB16F, 1, 1, 0, GL_RGB, GL_FLOAT, &bgcolor); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: #define XFORM_LINE_WIDTH OBJ_LINE_WIDTH nuclear@0: void GPUScene::create_xform_texture() nuclear@0: { nuclear@0: int tex_xsz = XFORM_LINE_WIDTH; nuclear@0: int tex_ysz = next_pow2((int)objects.size() + 1); // leave space for the descriptor nuclear@0: nuclear@0: glBindTexture(GL_TEXTURE_2D, textures[TEX_XFORM]); nuclear@0: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, tex_xsz, tex_ysz, 0, GL_RGBA, GL_FLOAT, 0); nuclear@0: nuclear@0: update_xform_texture(); nuclear@0: } nuclear@0: nuclear@0: void GPUScene::update_xform_texture() nuclear@0: { nuclear@0: int tex_xsz = XFORM_LINE_WIDTH; nuclear@0: int tex_ysz = next_pow2((int)objects.size() + 1); // leave space for the descriptor nuclear@0: nuclear@0: if(!xform_buf) { nuclear@0: xform_buf = new Vector4[tex_xsz * tex_ysz]; nuclear@0: xform_buf[0].x = tex_ysz; // descriptor nuclear@0: } nuclear@0: nuclear@0: Vector4 *pixptr = xform_buf + tex_xsz; nuclear@0: for(auto obj : objects) { nuclear@0: for(int i=0; i<4; i++) { nuclear@0: pixptr[i] = obj->xform.get_column_vector(i); nuclear@0: pixptr[i + 4] = obj->inv_xform.get_column_vector(i); nuclear@0: } nuclear@0: pixptr += tex_xsz; nuclear@0: } nuclear@0: nuclear@0: glBindTexture(GL_TEXTURE_2D, textures[TEX_XFORM]); nuclear@0: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_xsz, tex_ysz, GL_RGBA, GL_FLOAT, xform_buf); nuclear@0: }