From: John Tsiombikas Date: Thu, 22 Sep 2016 06:00:12 +0000 (+0300) Subject: shadows, textures, resource managers... shaders... X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=antikythera;a=commitdiff_plain;h=ccc1a688b59e25bb934a0d3e2bbf477960068d4f shadows, textures, resource managers... shaders... --- diff --git a/Makefile b/Makefile index 8a5b897..616c71b 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,12 @@ src = $(wildcard src/*.cc) -obj = $(src:.cc=.o) +csrc = $(wildcard src/*.c) +obj = $(src:.cc=.o) $(csrc:.c=.o) dep = $(obj:.o=.d) bin = anti +CFLAGS = -pedantic -Wall -g CXXFLAGS = -std=c++11 -pedantic -Wall -g -LDFLAGS = $(libgl_$(sys)) -lm -lgmath +LDFLAGS = $(libgl_$(sys)) -lm -lgmath -lvmath -limago -lresman -lpthread sys = $(shell uname -s) libgl_Linux = -lGL -lGLU -lglut -lGLEW @@ -15,6 +17,9 @@ $(bin): $(obj) -include $(dep) +%.d: %.c + @$(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@ + %.d: %.cc @$(CPP) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@ diff --git a/sdr/shadow-notex.p.glsl b/sdr/shadow-notex.p.glsl new file mode 100644 index 0000000..dac400f --- /dev/null +++ b/sdr/shadow-notex.p.glsl @@ -0,0 +1,56 @@ +/* vi: set ft=glsl */ +uniform samplerCube envmap; +uniform sampler2DShadow shadowmap; + +varying vec3 vdir, ldir[3], normal; +varying vec4 shadow_tc; +varying vec3 wdir; + +#define KD gl_FrontMaterial.diffuse.rgb +#define KS gl_FrontMaterial.specular.rgb +#define SPOW gl_FrontMaterial.shininess + +#define LD(i) gl_LightSource[i].diffuse.rgb +#define LS(i) gl_LightSource[i].specular.rgb + +vec3 calc_diffuse(in vec3 n, in vec3 l, in vec3 lcol) +{ + float ndotl = max(dot(n, l), 0.0); + return KD * lcol * ndotl; +} + +vec3 calc_specular(in vec3 n, in vec3 l, in vec3 v, in vec3 lcol) +{ + vec3 h = normalize(l + v); + float ndoth = max(dot(n, h), 0.0); + return KS * lcol * pow(ndoth, SPOW); +} + +void main() +{ + float shadow = shadow2DProj(shadowmap, shadow_tc).x; + + vec3 n = normalize(normal); + vec3 v = normalize(vdir); + + vec3 l = normalize(ldir[0]); + vec3 diffuse = calc_diffuse(n, l, LD(0)) * shadow; + vec3 specular = calc_specular(n, l, v, LS(0)) * shadow; + + l = normalize(ldir[1]); + diffuse += calc_diffuse(n, l, LD(1)); + specular += calc_specular(n, l, v, LS(1)); + + l = normalize(ldir[2]); + diffuse += calc_diffuse(n, l, LD(2)); + specular += calc_specular(n, l, v, LS(2)); + + // envmap + vec3 rdir = -reflect(wdir, n); + vec3 env_texel = textureCube(envmap, rdir).xyz; + specular += KS * env_texel; + + vec3 ambient = gl_LightModel.ambient.rgb * KD; + gl_FragColor.rgb = ambient + diffuse + specular; + gl_FragColor.a = gl_FrontMaterial.diffuse.a; +} diff --git a/sdr/shadow.v.glsl b/sdr/shadow.v.glsl new file mode 100644 index 0000000..8f9f045 --- /dev/null +++ b/sdr/shadow.v.glsl @@ -0,0 +1,27 @@ +uniform mat4 envmap_matrix; + +varying vec3 vdir, ldir[3], normal; +varying vec4 shadow_tc; +varying vec3 wdir; + +void main() +{ + gl_Position = ftransform(); + + vec3 vpos = (gl_ModelViewMatrix * gl_Vertex).xyz; + normal = gl_NormalMatrix * gl_Normal; + vdir = -vpos; + wdir = (envmap_matrix * vec4(vdir, 1.0)).xyz; // bring back to world space + ldir[0] = gl_LightSource[0].position.xyz - vpos; + ldir[1] = gl_LightSource[1].position.xyz - vpos; + ldir[2] = gl_LightSource[2].position.xyz - vpos; + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + + mat4 offmat = mat4(0.5, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.5, 0.5, 0.5, 1.0); + mat4 tex_matrix = offmat * gl_TextureMatrix[1]; + + shadow_tc = tex_matrix * vec4(vpos, 1.0); +} diff --git a/sdr/skydome.p.glsl b/sdr/skydome.p.glsl new file mode 100644 index 0000000..7682150 --- /dev/null +++ b/sdr/skydome.p.glsl @@ -0,0 +1,12 @@ +uniform samplerCube envmap; + +varying vec3 normal; + +void main() +{ + vec3 n = normalize(normal); + vec3 texel = textureCube(envmap, n).xyz; + + gl_FragColor.rgb = texel; + gl_FragColor.a = 1.0; +} diff --git a/sdr/skydome.v.glsl b/sdr/skydome.v.glsl new file mode 100644 index 0000000..c5d3124 --- /dev/null +++ b/sdr/skydome.v.glsl @@ -0,0 +1,7 @@ +varying vec3 normal; + +void main() +{ + gl_Position = ftransform(); + normal = gl_Normal; +} diff --git a/src/dataset.h b/src/dataset.h new file mode 100644 index 0000000..a4736c4 --- /dev/null +++ b/src/dataset.h @@ -0,0 +1,53 @@ +/** DataSet is a generic resource database with fast O(logn) lookups by name + * it can be used for texture managers, mesh managers, sound effect managers etc + * + * The constructor takes a load function and a destructor function to be called + * when a nonexistent resource is requested and needs to be loaded, and when + * the DataSet is destroyed. The destructor is optional and can be set to null + * if not needed. + * + * Requesting a resource works by simply calling get, example: + * ---------------------------------------------------------- + * \code + * Texture *load_texture(const char *fname); + * void free_texture(Texture *tex); + * + * DataSet texman(load_texture, free_texture); + * Texture *foo = texman.get("foo.png"); + * \endcode + */ +#ifndef DATASET_H_ +#define DATASET_H_ + +#include +#include +#include + +template +class DataSet { +protected: + mutable std::map data; + mutable struct resman *rman; + + T (*create)(); + bool (*load)(T, const char*); + bool (*done)(T); + void (*destroy)(T); + + static int dataset_load_func(const char *fname, int id, void *cls); + static int dataset_done_func(int id, void *cls); + static void dataset_destroy_func(int id, void *cls); + +public: + DataSet(T (*create_func)(), bool (*load_func)(T, const char*), bool (*done_func)(T) = 0, void (*destr_func)(T) = 0); + ~DataSet(); + + void clear(); + void update(); + + T get(const char *name) const; +}; + +#include "dataset.inl" + +#endif // DATASET_H_ diff --git a/src/dataset.inl b/src/dataset.inl new file mode 100644 index 0000000..5f0fbf9 --- /dev/null +++ b/src/dataset.inl @@ -0,0 +1,96 @@ +#include +#include + +template +DataSet::DataSet(T (*create_func)(), bool (*load_func)(T, const char*), bool (*done_func)(T), void (*destr_func)(T)) +{ + create = create_func; + load = load_func; + done = done_func; + destroy = destr_func; + + rman = resman_create(); + resman_set_load_func(rman, dataset_load_func, this); + resman_set_done_func(rman, dataset_done_func, this); + resman_set_destroy_func(rman, dataset_destroy_func, this); +} + +template +DataSet::~DataSet() +{ + resman_free(rman); +} + +template +void DataSet::clear() +{ + resman_free(rman); + data.clear(); + + rman = resman_create(); +} + +template +void DataSet::update() +{ + resman_poll(rman); +} + +template +T DataSet::get(const char *name) const +{ + typename std::map::const_iterator iter = data.find(name); + if(iter != data.end()) { + return iter->second; + } + + T res = create(); + resman_lookup(rman, name, res); + return res; +} + + +// --- static functions to pass as callback to resman --- + +template +int DataSet::dataset_load_func(const char *fname, int id, void *cls) +{ + DataSet *dset = (DataSet*)cls; + T data = (T)resman_get_res_data(dset->rman, id); + + if(!dset->load(data, fname)) { + return -1; + } + return 0; +} + +template +int DataSet::dataset_done_func(int id, void *cls) +{ + DataSet *dset = (DataSet*)cls; + + T data = (T)resman_get_res_data(dset->rman, id); + int load_res = resman_get_res_result(dset->rman, id); + + if(load_res != 0) { + fprintf(stderr, "failed to load resource %d (%s)\n", id, resman_get_res_name(dset->rman, id)); + } else { + printf("done loading resource %d (%s)\n", id, resman_get_res_name(dset->rman, id)); + } + + if(dset->done) { + dset->done(data); + } + return 0; +} + +template +void DataSet::dataset_destroy_func(int id, void *cls) +{ + DataSet *dset = (DataSet*)cls; + T data = (T)resman_get_res_data(dset->rman, id); + + if(dset->destroy) { + dset->destroy(data); + } +} diff --git a/src/gear.cc b/src/gear.cc index 697610d..c00b411 100644 --- a/src/gear.cc +++ b/src/gear.cc @@ -19,6 +19,10 @@ Gear::Gear() supergear = 0; mesh = 0; + + color = Vec3(0.6, 0.6, 0.6); + roughness = 1.0; + metallic = false; } Gear::~Gear() @@ -94,12 +98,16 @@ void Gear::set_position(const Vec3 &pos) { if(!supergear) { this->pos = pos; - xform_valid = false; } else { - if(fabs(this->pos.z - pos.z) > 1e-5) { - this->pos.z = pos.z; - xform_valid = false; - } + this->pos = supergear->pos; + this->pos.z = pos.z; + } + xform_valid = false; + + for(int i=0; i<(int)subgears.size(); i++) { + Vec3 subpos = this->pos; + subpos.z = subgears[i]->pos.z; + subgears[i]->set_position(subpos); } } @@ -164,7 +172,7 @@ void Gear::draw() const calc_matrix(); } - glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT); + glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_LIGHTING_BIT); glPushMatrix(); glMultMatrixf(xform[0]); @@ -174,6 +182,15 @@ void Gear::draw() const glEnable(GL_POLYGON_OFFSET_FILL); } + Vec3 diffuse = metallic ? Vec3(0, 0, 0) : color; + float col[] = {diffuse.x, diffuse.y, diffuse.z, 1.0}; + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, col); + + Vec3 specular = (metallic ? color : Vec3(1, 1, 1)) * (1.0 - roughness); + float scol[] = {specular.x, specular.y, specular.z, 1.0}; + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, scol); + glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 1.0 + (1.0 - roughness) * 60.0); + mesh->draw(); glDisable(GL_LIGHTING); @@ -183,13 +200,6 @@ void Gear::draw() const mesh->draw_wire(); } - glLineWidth(2.0); - glBegin(GL_LINES); - glColor3f(0, 0, 1); - glVertex3f(0, 0, -10); - glVertex3f(0, 0, 10); - glEnd(); - glPopMatrix(); glPopAttrib(); } diff --git a/src/gear.h b/src/gear.h index 1247a14..f6058a1 100644 --- a/src/gear.h +++ b/src/gear.h @@ -65,6 +65,11 @@ public: float bevel; // bevel size + // visual surface properties + Vec3 color; + float roughness; + bool metallic; + Gear *supergear; std::vector subgears; std::vector pins; diff --git a/src/image.cc b/src/image.cc new file mode 100644 index 0000000..c245212 --- /dev/null +++ b/src/image.cc @@ -0,0 +1,271 @@ +#include + +#ifndef _MSC_VER +#include +#else +#include +#endif + +#include "imago2.h" +#include "image.h" + +static int pixel_elements(Image::Format fmt); +static int elem_size(Image::Format fmt); +static int pixel_size(Image::Format fmt); + +Image::Image() +{ + fmt = FMT_RGBA; + width = height = 0; + pixels = 0; +} + +Image::~Image() +{ + delete [] (char*)pixels; +} + +int Image::get_width() const +{ + return width; +} + +int Image::get_height() const +{ + return height; +} + +Image::Format Image::get_format() const +{ + return fmt; +} + +bool Image::create(int x, int y, Format fmt) +{ + width = x; + height = y; + this->fmt = fmt; + + try { + pixels = new char[x * y * pixel_size(fmt)]; + } + catch(...) { + return false; + } + return true; +} + +bool Image::set_pixels(int xsz, int ysz, void *pixels, Format fmt) +{ + if(!create(xsz, ysz, fmt)) { + return false; + } + memcpy(this->pixels, pixels, xsz * ysz * pixel_size(fmt)); + return true; +} + +bool Image::set_pixels(int xsz, int ysz, void *pixels, int scan_width, Format fmt) +{ + return set_pixels(xsz, ysz, pixels, 0, 0, scan_width, fmt); +} + +bool Image::set_pixels(int xsz, int ysz, void *pixels, int x, int y, int scan_width, Format fmt) +{ + if(scan_width <= 0) { + scan_width = xsz; + } + + if(!create(xsz, ysz, fmt)) { + return false; + } + + int pixsz = pixel_size(fmt); + + unsigned char *dest = (unsigned char*)this->pixels; + unsigned char *src = (unsigned char*)pixels + (y * scan_width + x) * pixsz; + for(int i=0; i dest) { + memcpy(tmppix, src, pixsz); + memcpy(src, dest, pixsz); + memcpy(dest, tmppix, pixsz); + dest += pixsz; + src -= pixsz; + } + + scan += width * pixsz; + } +} + +void Image::flip_vertical() +{ + int pixsz = pixel_size(fmt); + + unsigned char *tmpscan = (unsigned char*)alloca(width * pixsz); + + unsigned char *dest = (unsigned char*)pixels; + unsigned char *src = (unsigned char*)pixels + (height - 1) * width * pixsz; + + while(src > dest) { + memcpy(tmpscan, src, width * pixsz); + memcpy(src, dest, width * pixsz); + memcpy(dest, tmpscan, width * pixsz); + dest += width * pixsz; + src -= width * pixsz; + } +} + +void Image::rotate_180() +{ + flip_vertical(); + flip_horizontal(); +} + +bool Image::load(const char *fname) +{ + struct img_pixmap pixmap; + + img_init(&pixmap); + if(img_load(&pixmap, fname) == -1) { + return false; + } + + Format fmt; + switch(pixmap.fmt) { + case IMG_FMT_GREY8: + fmt = FMT_GREY; + break; + case IMG_FMT_RGB24: + fmt = FMT_RGB; + break; + case IMG_FMT_RGBA32: + fmt = FMT_RGBA; + break; + case IMG_FMT_GREYF: + fmt = FMT_GREY_FLOAT; + break; + case IMG_FMT_RGBF: + fmt = FMT_RGB_FLOAT; + break; + case IMG_FMT_RGBAF: + fmt = FMT_RGBA_FLOAT; + break; + default: + img_destroy(&pixmap); + return false; + } + + if(!set_pixels(pixmap.width, pixmap.height, pixmap.pixels, fmt)) { + img_destroy(&pixmap); + return false; + } + img_destroy(&pixmap); + return true; +} + +bool Image::save(const char *fname) const +{ + struct img_pixmap pixmap; + + img_init(&pixmap); + + switch(fmt) { + case FMT_GREY: + pixmap.fmt = IMG_FMT_GREY8; + break; + case FMT_GREY_FLOAT: + pixmap.fmt = IMG_FMT_GREYF; + break; + case FMT_RGB: + pixmap.fmt = IMG_FMT_RGB24; + break; + case FMT_RGB_FLOAT: + pixmap.fmt = IMG_FMT_RGBF; + break; + case FMT_RGBA: + pixmap.fmt = IMG_FMT_RGBA32; + break; + case FMT_RGBA_FLOAT: + pixmap.fmt = IMG_FMT_RGBAF; + break; + default: + return false; + } + + pixmap.width = width; + pixmap.height = height; + pixmap.pixels = pixels; + pixmap.pixelsz = pixel_size(fmt); + + if(img_save(&pixmap, fname) == -1) { + return false; + } + return true; +} + +static int pixel_elements(Image::Format fmt) +{ + switch(fmt) { + case Image::FMT_GREY: + case Image::FMT_GREY_FLOAT: + return 1; + + case Image::FMT_RGB: + case Image::FMT_RGB_FLOAT: + return 3; + + case Image::FMT_RGBA: + case Image::FMT_RGBA_FLOAT: + return 4; + + default: + break; + } + return 0; +} + +static int elem_size(Image::Format fmt) +{ + switch(fmt) { + case Image::FMT_GREY: + case Image::FMT_RGB: + case Image::FMT_RGBA: + return 1; + + case Image::FMT_GREY_FLOAT: + case Image::FMT_RGB_FLOAT: + case Image::FMT_RGBA_FLOAT: + return sizeof(float); + + default: + break; + } + return 0; +} + +static int pixel_size(Image::Format fmt) +{ + return elem_size(fmt) * pixel_elements(fmt); +} diff --git a/src/image.h b/src/image.h new file mode 100644 index 0000000..b812ae1 --- /dev/null +++ b/src/image.h @@ -0,0 +1,43 @@ +#ifndef IMAGE_H_ +#define IMAGE_H_ + +class Image { +public: + enum Format { + FMT_GREY, + FMT_RGB, + FMT_RGBA, + FMT_GREY_FLOAT, + FMT_RGB_FLOAT, + FMT_RGBA_FLOAT + }; + +private: + Format fmt; + int width, height; + void *pixels; + +public: + Image(); + ~Image(); + + int get_width() const; + int get_height() const; + + Format get_format() const; + + bool create(int x, int y, Format fmt = FMT_RGBA); + bool set_pixels(int xsz, int ysz, void *pixels, Format fmt = FMT_RGBA); + bool set_pixels(int xsz, int ysz, void *pixels, int scan_width, Format fmt = FMT_RGBA); + bool set_pixels(int xsz, int ysz, void *pixels, int x, int y, int scan_width = -1, Format fmt = FMT_RGBA); + void *get_pixels() const; + + void flip_horizontal(); + void flip_vertical(); + void rotate_180(); + + bool load(const char *fname); + bool save(const char *fname) const; +}; + +#endif // IMAGE_H_ diff --git a/src/machine.cc b/src/machine.cc index 313540d..3d4dd73 100644 --- a/src/machine.cc +++ b/src/machine.cc @@ -3,6 +3,7 @@ #include #include #include +#include "opengl.h" #include "machine.h" static float delta_angle(float a, float b); @@ -212,6 +213,19 @@ void Machine::draw() const for(size_t i=0; idraw(); } + + float dcol[] = {0.4, 0.4, 0.4, 1.0}; + float scol[] = {0, 0, 0, 0}; + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, dcol); + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, scol); + + glBegin(GL_QUADS); + glNormal3f(0, 1, 0); + glVertex3f(-300, -100, 300); + glVertex3f(300, -100, 300); + glVertex3f(300, -100, -300); + glVertex3f(-300, -100, -300); + glEnd(); } Gear *Machine::intersect_gear(const Ray &ray, HitPoint *hitp) const diff --git a/src/main.cc b/src/main.cc index 5dd2857..772b63c 100644 --- a/src/main.cc +++ b/src/main.cc @@ -8,12 +8,17 @@ #include #endif #include "app.h" +#include "sdr.h" +#include "shadow.h" +#include "texture.h" #include "machine.h" +#include "meshgen.h" static bool init(); static void cleanup(); static void display(); static void idle(); +static void draw_scene(); static void draw_gears(); static void reshape(int x, int y); static void keyb(unsigned char key, int x, int y); @@ -24,17 +29,28 @@ static Gear *pick_gear(int x, int y); static int win_width, win_height; -static float cam_dist = 0.5; -static float cam_theta, cam_phi; +static float cam_dist = 0.25; +static float cam_theta, cam_phi = 20; static int prev_mx, prev_my; static bool bnstate[8]; +static Mat4 view_matrix; + static unsigned int start_time, prev_msec; static Machine *machine; static Gear *hover_gear, *sel_gear; static HitPoint pick_hit; static Vec3 sel_hit_pos; +static unsigned int sdr_shadow_notex; +static int dbg_show_shadowmap; + +static TextureSet texman; +static Texture *envmap; +static Mesh *skydome; +static unsigned int sdr_skydome; + + int main(int argc, char **argv) { glutInitWindowSize(1024, 768); @@ -79,6 +95,7 @@ static bool init() gear1->pos = Vec3(-50, 0, 0); gear1->set_teeth(16, pitch); gear1->gen_mesh(); + gear1->color = Vec3(0.35, 0.6, 0.8); machine->add_gear(gear1); Gear *gear2 = new Gear; @@ -93,6 +110,7 @@ static bool init() gear3->pos = gear2->pos + Vec3(0, gear2->radius + gear3->radius - gear2->teeth_length * 0.75, 0); gear3->gen_mesh(); machine->add_gear(gear3); + gear3->color = Vec3(0.5, 0.8, 0.6); Gear *subgear = new Gear; subgear->set_teeth(10, pitch); @@ -100,20 +118,48 @@ static bool init() subgear->gen_mesh(); gear2->attach(subgear); machine->add_gear(subgear); + subgear->color = Vec3(0.8, 0.7, 0.5); machine->add_motor(0, 1.0); + // shadows + init_shadow(2048); + + if(!(sdr_shadow_notex = create_program_load("sdr/shadow.v.glsl", "sdr/shadow-notex.p.glsl"))) { + return false; + } + set_uniform_int(sdr_shadow_notex, "shadowmap", 1); + set_uniform_int(sdr_shadow_notex, "envmap", 2); + + float ambient[] = {0.1, 0.1, 0.1, 0.0}; + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); + + // env + envmap = texman.get_texture("data/stpeters_cross.jpg", TEX_CUBE); + + skydome = new Mesh; + gen_sphere(skydome, 1.0, 16, 8); + skydome->flip_faces(); + + if(!(sdr_skydome = create_program_load("sdr/skydome.v.glsl", "sdr/skydome.p.glsl"))) { + return false; + } + glUseProgram(0); + start_time = glutGet(GLUT_ELAPSED_TIME); return true; } static void cleanup() { + texman.clear(); delete machine; } static void update(float dt) { + texman.update(); + machine->update(dt); if(sel_gear) { @@ -142,19 +188,87 @@ static void display() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + view_matrix = Mat4::identity; + view_matrix.pre_translate(0, 0, -cam_dist); + view_matrix.pre_rotate(deg_to_rad(cam_phi), 1, 0, 0); + view_matrix.pre_rotate(deg_to_rad(cam_theta), 0, 1, 0); + glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glTranslatef(0, 0, -cam_dist); - glRotatef(cam_phi, 1, 0, 0); - glRotatef(cam_theta, 0, 1, 0); + glLoadMatrixf(view_matrix[0]); - set_light(0, Vec3(-50, 75, 100), Vec3(1.0, 0.8, 0.7) * 0.8); - set_light(1, Vec3(100, 0, 30), Vec3(0.6, 0.7, 1.0) * 0.6); - set_light(2, Vec3(-10, -10, 60), Vec3(0.8, 1.0, 0.8) * 0.3); + static const Vec3 lpos[] = { Vec3(-50, 75, 100), Vec3(100, 0, 30), Vec3(-10, -10, 60) }; + set_light(0, lpos[0], Vec3(1.0, 0.8, 0.7) * 0.8); + set_light(1, lpos[1], Vec3(0.6, 0.7, 1.0) * 0.6); + set_light(2, lpos[2], Vec3(0.8, 1.0, 0.8) * 0.3); update(dt); - draw_gears(); + // shadowmap pass + begin_shadow_pass(lpos[0], Vec3(0, 0, 0), 0.2, 100, 150); + draw_scene(); + end_shadow_pass(); + + // regular pass + const Mat4 &shadow_matrix = get_shadow_matrix(); + Mat4 env_matrix = transpose(view_matrix.upper3x3()); + set_uniform_matrix4(sdr_shadow_notex, "envmap_matrix", env_matrix[0]); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, get_shadow_tex()); + glMatrixMode(GL_TEXTURE); + glLoadMatrixf(shadow_matrix[0]); + + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_CUBE_MAP, envmap->get_id()); + + glActiveTexture(GL_TEXTURE0); + glMatrixMode(GL_MODELVIEW); + + draw_scene(); + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + + + if(dbg_show_shadowmap) { + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + + glPushAttrib(GL_ENABLE_BIT); + glUseProgram(0); + glBindTexture(GL_TEXTURE_2D, get_shadow_tex()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); + glEnable(GL_TEXTURE_2D); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glDisable(GL_BLEND); + + glBegin(GL_QUADS); + glColor4f(1, 1, 1, 1); + glTexCoord2f(0, 0); glVertex2f(-0.95, -0.95); + glTexCoord2f(1, 0); glVertex2f(-0.5, -0.95); + glTexCoord2f(1, 1); glVertex2f(-0.5, -0.5); + glTexCoord2f(0, 1); glVertex2f(-0.95, -0.5); + glEnd(); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); + glBindTexture(GL_TEXTURE_2D, 0); + + glPopAttrib(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + } + glutSwapBuffers(); assert(glGetError() == GL_NO_ERROR); @@ -165,13 +279,38 @@ static void idle() glutPostRedisplay(); } +static void draw_scene() +{ + // draw skydome + glDepthMask(0); + + Mat4 rot_view = view_matrix.upper3x3(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixf(rot_view[0]); + + bind_texture(envmap, 0); + + glUseProgram(sdr_skydome); + skydome->draw(); + glUseProgram(0); + + bind_texture(0, 0); + + glPopMatrix(); + glDepthMask(1); + + // draw mechanism + draw_gears(); +} + static void draw_gears() { /* world scale is in meters, gears are in millimeters, scale by 1/1000 */ glPushMatrix(); glScalef(0.001, 0.001, 0.001); - if(sel_gear || hover_gear) { + if(!shadow_pass && (sel_gear || hover_gear)) { glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_POLYGON_BIT); glDisable(GL_LIGHTING); @@ -190,7 +329,9 @@ static void draw_gears() glPopAttrib(); } + glUseProgram(shadow_pass ? 0 : sdr_shadow_notex); machine->draw(); + glUseProgram(0); glPopMatrix(); } @@ -204,7 +345,7 @@ static void reshape(int x, int y) glMatrixMode(GL_PROJECTION); glLoadIdentity(); - gluPerspective(50.0, (float)x / (float)y, 0.01, 100.0); + gluPerspective(50.0, (float)x / (float)y, 0.01, 50.0); } static void keyb(unsigned char key, int x, int y) @@ -217,6 +358,11 @@ static void keyb(unsigned char key, int x, int y) opt_gear_wireframe = !opt_gear_wireframe; glutPostRedisplay(); break; + + case 's': + dbg_show_shadowmap = !dbg_show_shadowmap; + glutPostRedisplay(); + break; } } diff --git a/src/sdr.c b/src/sdr.c new file mode 100644 index 0000000..0152a6e --- /dev/null +++ b/src/sdr.c @@ -0,0 +1,562 @@ +#include +#include +#include +#include +#include +#include +#include "opengl.h" + +#if defined(unix) || defined(__unix__) +#include +#include +#endif /* unix */ + +#include "sdr.h" + +static const char *sdrtypestr(unsigned int sdrtype); +static int sdrtypeidx(unsigned int sdrtype); + + +unsigned int create_vertex_shader(const char *src) +{ + return create_shader(src, GL_VERTEX_SHADER); +} + +unsigned int create_pixel_shader(const char *src) +{ + return create_shader(src, GL_FRAGMENT_SHADER); +} + +unsigned int create_tessctl_shader(const char *src) +{ +#ifdef GL_TESS_CONTROL_SHADER + return create_shader(src, GL_TESS_CONTROL_SHADER); +#else + return 0; +#endif +} + +unsigned int create_tesseval_shader(const char *src) +{ +#ifdef GL_TESS_EVALUATION_SHADER + return create_shader(src, GL_TESS_EVALUATION_SHADER); +#else + return 0; +#endif +} + +unsigned int create_geometry_shader(const char *src) +{ +#ifdef GL_GEOMETRY_SHADER + return create_shader(src, GL_GEOMETRY_SHADER); +#else + return 0; +#endif +} + +unsigned int create_shader(const char *src, unsigned int sdr_type) +{ + unsigned int sdr; + int success, info_len; + char *info_str = 0; + const char *src_str[3], *header, *footer; + int src_str_count = 0; + GLenum err; + + if((header = get_shader_header(sdr_type))) { + src_str[src_str_count++] = header; + } + src_str[src_str_count++] = src; + if((footer = get_shader_footer(sdr_type))) { + src_str[src_str_count++] = footer; + } + + sdr = glCreateShader(sdr_type); + assert(glGetError() == GL_NO_ERROR); + glShaderSource(sdr, src_str_count, src_str, 0); + err = glGetError(); + assert(err == GL_NO_ERROR); + glCompileShader(sdr); + assert(glGetError() == GL_NO_ERROR); + + glGetShaderiv(sdr, GL_COMPILE_STATUS, &success); + assert(glGetError() == GL_NO_ERROR); + glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &info_len); + assert(glGetError() == GL_NO_ERROR); + + if(info_len) { + if((info_str = malloc(info_len + 1))) { + glGetShaderInfoLog(sdr, info_len, 0, info_str); + assert(glGetError() == GL_NO_ERROR); + info_str[info_len] = 0; + } + } + + if(success) { + fprintf(stderr, info_str ? "done: %s\n" : "done\n", info_str); + } else { + fprintf(stderr, info_str ? "failed: %s\n" : "failed\n", info_str); + glDeleteShader(sdr); + sdr = 0; + } + + free(info_str); + return sdr; +} + +void free_shader(unsigned int sdr) +{ + glDeleteShader(sdr); +} + +unsigned int load_vertex_shader(const char *fname) +{ + return load_shader(fname, GL_VERTEX_SHADER); +} + +unsigned int load_pixel_shader(const char *fname) +{ + return load_shader(fname, GL_FRAGMENT_SHADER); +} + +unsigned int load_tessctl_shader(const char *fname) +{ +#ifdef GL_TESS_CONTROL_SHADER + return load_shader(fname, GL_TESS_CONTROL_SHADER); +#else + return 0; +#endif +} + +unsigned int load_tesseval_shader(const char *fname) +{ +#ifdef GL_TESS_EVALUATION_SHADER + return load_shader(fname, GL_TESS_EVALUATION_SHADER); +#else + return 0; +#endif +} + +unsigned int load_geometry_shader(const char *fname) +{ +#ifdef GL_GEOMETRY_SHADER + return load_shader(fname, GL_GEOMETRY_SHADER); +#else + return 0; +#endif +} + +unsigned int load_shader(const char *fname, unsigned int sdr_type) +{ + unsigned int sdr; + size_t filesize; + FILE *fp; + char *src; + + if(!(fp = fopen(fname, "rb"))) { + fprintf(stderr, "failed to open shader %s: %s\n", fname, strerror(errno)); + return 0; + } + + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fseek(fp, 0, SEEK_SET); + + if(!(src = malloc(filesize + 1))) { + fclose(fp); + return 0; + } + fread(src, 1, filesize, fp); + src[filesize] = 0; + fclose(fp); + + fprintf(stderr, "compiling %s shader: %s... ", sdrtypestr(sdr_type), fname); + sdr = create_shader(src, sdr_type); + + free(src); + return sdr; +} + + +/* ---- gpu programs ---- */ + +unsigned int create_program(void) +{ + unsigned int prog = glCreateProgram(); + assert(glGetError() == GL_NO_ERROR); + return prog; +} + +unsigned int create_program_link(unsigned int sdr0, ...) +{ + unsigned int prog, sdr; + va_list ap; + + if(!(prog = create_program())) { + return 0; + } + + attach_shader(prog, sdr0); + if(glGetError()) { + return 0; + } + + va_start(ap, sdr0); + while((sdr = va_arg(ap, unsigned int))) { + attach_shader(prog, sdr); + if(glGetError()) { + return 0; + } + } + va_end(ap); + + if(link_program(prog) == -1) { + free_program(prog); + return 0; + } + return prog; +} + +unsigned int create_program_load(const char *vfile, const char *pfile) +{ + unsigned int vs = 0, ps = 0; + + if(vfile && *vfile && !(vs = load_vertex_shader(vfile))) { + return 0; + } + if(pfile && *pfile && !(ps = load_pixel_shader(pfile))) { + return 0; + } + return create_program_link(vs, ps, 0); +} + +void free_program(unsigned int sdr) +{ + glDeleteProgram(sdr); +} + +void attach_shader(unsigned int prog, unsigned int sdr) +{ + int err; + + if(prog && sdr) { + assert(glGetError() == GL_NO_ERROR); + glAttachShader(prog, sdr); + if((err = glGetError()) != GL_NO_ERROR) { + fprintf(stderr, "failed to attach shader %u to program %u (err: 0x%x)\n", sdr, prog, err); + abort(); + } + } +} + +int link_program(unsigned int prog) +{ + int linked, info_len, retval = 0; + char *info_str = 0; + + glLinkProgram(prog); + assert(glGetError() == GL_NO_ERROR); + glGetProgramiv(prog, GL_LINK_STATUS, &linked); + assert(glGetError() == GL_NO_ERROR); + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &info_len); + assert(glGetError() == GL_NO_ERROR); + + if(info_len) { + if((info_str = malloc(info_len + 1))) { + glGetProgramInfoLog(prog, info_len, 0, info_str); + assert(glGetError() == GL_NO_ERROR); + info_str[info_len] = 0; + } + } + + if(linked) { + fprintf(stderr, info_str ? "linking done: %s\n" : "linking done\n", info_str); + } else { + fprintf(stderr, info_str ? "linking failed: %s\n" : "linking failed\n", info_str); + retval = -1; + } + + free(info_str); + return retval; +} + +int bind_program(unsigned int prog) +{ + GLenum err; + + glUseProgram(prog); + if(prog && (err = glGetError()) != GL_NO_ERROR) { + /* maybe the program is not linked, try linking first */ + if(err == GL_INVALID_OPERATION) { + if(link_program(prog) == -1) { + return -1; + } + glUseProgram(prog); + return glGetError() == GL_NO_ERROR ? 0 : -1; + } + return -1; + } + return 0; +} + +/* ugly but I'm not going to write the same bloody code over and over */ +#define BEGIN_UNIFORM_CODE \ + int loc, curr_prog; \ + glGetIntegerv(GL_CURRENT_PROGRAM, &curr_prog); \ + if((unsigned int)curr_prog != prog && bind_program(prog) == -1) { \ + return -1; \ + } \ + if((loc = glGetUniformLocation(prog, name)) != -1) + +#define END_UNIFORM_CODE \ + if((unsigned int)curr_prog != prog) { \ + bind_program(curr_prog); \ + } \ + return loc == -1 ? -1 : 0 + +int get_uniform_loc(unsigned int prog, const char *name) +{ + int loc, curr_prog; + glGetIntegerv(GL_CURRENT_PROGRAM, &curr_prog); + if((unsigned int)curr_prog != prog && bind_program(prog) == -1) { + return -1; + } + loc = glGetUniformLocation(prog, name); + if((unsigned int)curr_prog != prog) { + bind_program(curr_prog); + } + return loc; +} + +int set_uniform_int(unsigned int prog, const char *name, int val) +{ + BEGIN_UNIFORM_CODE { + glUniform1i(loc, val); + } + END_UNIFORM_CODE; +} + +int set_uniform_float(unsigned int prog, const char *name, float val) +{ + BEGIN_UNIFORM_CODE { + glUniform1f(loc, val); + } + END_UNIFORM_CODE; +} + +int set_uniform_float2(unsigned int prog, const char *name, float x, float y) +{ + BEGIN_UNIFORM_CODE { + glUniform2f(loc, x, y); + } + END_UNIFORM_CODE; +} + +int set_uniform_float3(unsigned int prog, const char *name, float x, float y, float z) +{ + BEGIN_UNIFORM_CODE { + glUniform3f(loc, x, y, z); + } + END_UNIFORM_CODE; +} + +int set_uniform_float4(unsigned int prog, const char *name, float x, float y, float z, float w) +{ + BEGIN_UNIFORM_CODE { + glUniform4f(loc, x, y, z, w); + } + END_UNIFORM_CODE; +} + +int set_uniform_matrix4(unsigned int prog, const char *name, const float *mat) +{ + BEGIN_UNIFORM_CODE { + glUniformMatrix4fv(loc, 1, GL_FALSE, mat); + } + END_UNIFORM_CODE; +} + +int set_uniform_matrix4_transposed(unsigned int prog, const char *name, const float *mat) +{ + BEGIN_UNIFORM_CODE { + glUniformMatrix4fv(loc, 1, GL_TRUE, mat); + } + END_UNIFORM_CODE; +} + +int get_attrib_loc(unsigned int prog, const char *name) +{ + int loc, curr_prog; + + glGetIntegerv(GL_CURRENT_PROGRAM, &curr_prog); + if((unsigned int)curr_prog != prog && bind_program(prog) == -1) { + return -1; + } + + loc = glGetAttribLocation(prog, (char*)name); + + if((unsigned int)curr_prog != prog) { + bind_program(curr_prog); + } + return loc; +} + +void set_attrib_float3(int attr_loc, float x, float y, float z) +{ + glVertexAttrib3f(attr_loc, x, y, z); +} + +/* ---- shader composition ---- */ +struct string { + char *text; + int len; +}; + +#define NUM_SHADER_TYPES 5 +static struct string header[NUM_SHADER_TYPES]; +static struct string footer[NUM_SHADER_TYPES]; + +static void clear_string(struct string *str) +{ + free(str->text); + str->text = 0; + str->len = 0; +} + +static void append_string(struct string *str, const char *s) +{ + int len, newlen; + char *newstr; + + if(!s || !*s) return; + + len = strlen(s); + newlen = str->len + len; + if(!(newstr = malloc(newlen + 2))) { /* leave space for a possible newline */ + fprintf(stderr, "shader composition: failed to append string of size %d\n", len); + abort(); + } + + if(str->text) { + memcpy(newstr, str->text, str->len); + } + memcpy(newstr + str->len, s, len + 1); + + if(s[len - 1] != '\n') { + newstr[newlen] = '\n'; + newstr[newlen + 1] = 0; + } + + free(str->text); + str->text = newstr; + str->len = newlen; +} + +void clear_shader_header(unsigned int type) +{ + if(type) { + int idx = sdrtypeidx(type); + clear_string(&header[idx]); + } else { + int i; + for(i=0; i"; +} + +static int sdrtypeidx(unsigned int sdrtype) +{ + switch(sdrtype) { + case GL_VERTEX_SHADER: + return 0; + case GL_FRAGMENT_SHADER: + return 1; + case GL_TESS_CONTROL_SHADER: + return 2; + case GL_TESS_EVALUATION_SHADER: + return 3; + case GL_GEOMETRY_SHADER: + return 4; + default: + break; + } + return 0; +} diff --git a/src/sdr.h b/src/sdr.h new file mode 100644 index 0000000..7bf2389 --- /dev/null +++ b/src/sdr.h @@ -0,0 +1,68 @@ +#ifndef SDR_H_ +#define SDR_H_ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* ---- shaders ---- */ +unsigned int create_vertex_shader(const char *src); +unsigned int create_pixel_shader(const char *src); +unsigned int create_tessctl_shader(const char *src); +unsigned int create_tesseval_shader(const char *src); +unsigned int create_geometry_shader(const char *src); +unsigned int create_shader(const char *src, unsigned int sdr_type); +void free_shader(unsigned int sdr); + +unsigned int load_vertex_shader(const char *fname); +unsigned int load_pixel_shader(const char *fname); +unsigned int load_tessctl_shader(const char *fname); +unsigned int load_tesseval_shader(const char *fname); +unsigned int load_geometry_shader(const char *fname); +unsigned int load_shader(const char *src, unsigned int sdr_type); + +int add_shader(const char *fname, unsigned int sdr); +int remove_shader(const char *fname); + +/* ---- gpu programs ---- */ +unsigned int create_program(void); +unsigned int create_program_link(unsigned int sdr0, ...); +unsigned int create_program_load(const char *vfile, const char *pfile); +void free_program(unsigned int sdr); + +void attach_shader(unsigned int prog, unsigned int sdr); +int link_program(unsigned int prog); +int bind_program(unsigned int prog); + +int get_uniform_loc(unsigned int prog, const char *name); + +int set_uniform_int(unsigned int prog, const char *name, int val); +int set_uniform_float(unsigned int prog, const char *name, float val); +int set_uniform_float2(unsigned int prog, const char *name, float x, float y); +int set_uniform_float3(unsigned int prog, const char *name, float x, float y, float z); +int set_uniform_float4(unsigned int prog, const char *name, float x, float y, float z, float w); +int set_uniform_matrix4(unsigned int prog, const char *name, const float *mat); +int set_uniform_matrix4_transposed(unsigned int prog, const char *name, const float *mat); + +int get_attrib_loc(unsigned int prog, const char *name); +void set_attrib_float3(int attr_loc, float x, float y, float z); + +/* ---- shader composition ---- */ + +/* clear shader header/footer text. + * pass the shader type to clear, or 0 to clear all types */ +void clear_shader_header(unsigned int type); +void clear_shader_footer(unsigned int type); +/* append text to the header/footer of a specific shader type + * or use type 0 to add it to all shade types */ +void add_shader_header(unsigned int type, const char *s); +void add_shader_footer(unsigned int type, const char *s); +/* get the current header/footer text for a specific shader type */ +const char *get_shader_header(unsigned int type); +const char *get_shader_footer(unsigned int type); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SDR_H_ */ diff --git a/src/shadow.cc b/src/shadow.cc new file mode 100644 index 0000000..357ed82 --- /dev/null +++ b/src/shadow.cc @@ -0,0 +1,147 @@ +#define GPH_NAMESPACE +#include +#include "opengl.h" +#include "shadow.h" +#include "vmath/vmath.h" + + +bool shadow_pass; + +static int tex_sz, prev_vp[4]; +static unsigned int fbo, depth_tex, rb_color; +static gph::Mat4 shadow_mat; + +bool init_shadow(int sz) +{ + tex_sz = sz; + printf("initializing shadow buffer (%dx%d)\n", tex_sz, tex_sz); + + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + const float border_color[] = {1, 1, 1, 1}; + + glGenTextures(1, &depth_tex); + glBindTexture(GL_TEXTURE_2D, depth_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, tex_sz, tex_sz, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_tex, 0); + + assert(glGetError() == GL_NO_ERROR); + + glDrawBuffer(GL_FALSE); + glReadBuffer(GL_FALSE); + + if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + fprintf(stderr, "incomplete framebuffer\n"); + return false; + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDrawBuffer(GL_BACK); + glReadBuffer(GL_BACK); + assert(glGetError() == GL_NO_ERROR); + + return true; +} + +void destroy_shadow() +{ + glDeleteTextures(1, &depth_tex); + glDeleteRenderbuffers(1, &rb_color); + glDeleteFramebuffers(1, &fbo); +} + +void begin_shadow_pass(const gph::Vec3 &lpos, const gph::Vec3 <arg, float lfov, float znear, float zfar) +{ + shadow_pass = true; + + glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT); + glDisable(GL_LIGHTING); + glColorMask(0, 0, 0, 0); + glDepthMask(1); + + Matrix4x4 viewmat; + glGetFloatv(GL_MODELVIEW_MATRIX, viewmat[0]); + viewmat.transpose(); + + Matrix4x4 lt_viewmat, lt_projmat; + lt_projmat.set_perspective(DEG_TO_RAD(lfov) * 2.0, 1.0, znear, zfar); + lt_viewmat.set_lookat(Vector3(lpos.x, lpos.y, lpos.z), Vector3(ltarg.x, ltarg.y, ltarg.z), Vector3(0, 1, 0)); + Matrix4x4 smat = lt_projmat * lt_viewmat * viewmat.inverse(); + smat.transpose(); + + memcpy(shadow_mat[0], smat[0], 16 * sizeof(float)); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadTransposeMatrixf(lt_projmat[0]); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadTransposeMatrixf(lt_viewmat[0]); + + + /*gph::Mat4 viewmat; + glGetFloatv(GL_MODELVIEW_MATRIX, viewmat[0]); + + gph::Mat4 lt_viewmat, lt_projmat; + lt_projmat.perspective(deg_to_rad(lfov) * 2.0, 1.0, znear, zfar); + lt_viewmat.inv_lookat(lpos, ltarg, gph::Vec3(0, 1, 0)); + shadow_mat = lt_projmat * lt_viewmat * viewmat.inverse(); + //shadow_mat = viewmat.inverse() * lt_viewmat * lt_projmat; + shadow_mat.print(); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixf(lt_projmat[0]); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixf(lt_viewmat[0]); + */ + + glGetIntegerv(GL_VIEWPORT, prev_vp); + glViewport(0, 0, tex_sz, tex_sz); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + glPolygonOffset(2, 2); + glEnable(GL_POLYGON_OFFSET_FILL); + + glClear(GL_DEPTH_BUFFER_BIT); + glUseProgram(0); +} + + +void end_shadow_pass() +{ + shadow_pass = false; + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glViewport(prev_vp[0], prev_vp[1], prev_vp[2], prev_vp[3]); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + glPopAttrib(); +} + +gph::Mat4 get_shadow_matrix() +{ + return shadow_mat; +} + +unsigned int get_shadow_tex() +{ + return depth_tex; +} diff --git a/src/shadow.h b/src/shadow.h new file mode 100644 index 0000000..b40b6f2 --- /dev/null +++ b/src/shadow.h @@ -0,0 +1,17 @@ +#ifndef SHADOW_H_ +#define SHADOW_H_ + +#include + +extern bool shadow_pass; + +bool init_shadow(int sz); +void destroy_shadow(); + +void begin_shadow_pass(const gph::Vec3 &lpos, const gph::Vec3 <arg, float lfov, float znear, float zfar); +void end_shadow_pass(); + +gph::Mat4 get_shadow_matrix(); +unsigned int get_shadow_tex(); + +#endif // SHADOW_H_ diff --git a/src/texture.cc b/src/texture.cc new file mode 100644 index 0000000..a787abd --- /dev/null +++ b/src/texture.cc @@ -0,0 +1,504 @@ +#include +#include "texture.h" +#include "image.h" +#include "opengl.h" +#include "imago2.h" + +static int glifmt_from_ifmt(unsigned int ifmt); +static int glfmt_from_ifmt(unsigned int ifmt); +static int gltype_from_ifmt(unsigned int ifmt); + +static int glifmt_from_imgfmt(Image::Format fmt); + +static unsigned int type_to_target(TextureType type); +static TextureType target_to_type(unsigned int targ); + +static unsigned int cur_target[8] = { + GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, + GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D +}; + +static unsigned int cube_faces[] = { + GL_TEXTURE_CUBE_MAP_POSITIVE_X, + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z +}; + + +void bind_texture(Texture *tex, int tunit) +{ + if(tex) { + tex->bind(tunit); + } else { + glActiveTexture(GL_TEXTURE0 + tunit); + glBindTexture(cur_target[tunit], 0); + glActiveTexture(GL_TEXTURE0); + } +} + + +Image *Texture::default_img; + +Texture::Texture() +{ + target = 0; + sz[0] = sz[1] = sz[2] = 0; + texfmt = 0; + + img = 0; + glGenTextures(1, &id); +} + +Texture::~Texture() +{ + if(id) { + glDeleteTextures(1, &id); + } + if(img) { + delete img; + } +} + +void Texture::set_wrapping(unsigned int wrap) +{ + if(!target) { + return; + } + + glBindTexture(target, id); + glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap); + glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap); + glTexParameteri(target, GL_TEXTURE_WRAP_R, wrap); +} + +void Texture::set_filtering(unsigned int filt) +{ + unsigned int mag_filter; + + if(!target) { + return; + } + + switch(filt) { + case GL_LINEAR_MIPMAP_NEAREST: + case GL_LINEAR_MIPMAP_LINEAR: + mag_filter = GL_LINEAR; + break; + + case GL_NEAREST_MIPMAP_NEAREST: + case GL_NEAREST_MIPMAP_LINEAR: + mag_filter = GL_NEAREST; + break; + + default: + mag_filter = filt; + } + + set_filtering(filt, mag_filter); +} + +void Texture::set_filtering(unsigned int min_filt, unsigned int mag_filt) +{ + glBindTexture(target, id); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min_filt); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, mag_filt); +} + +unsigned int Texture::get_format() const +{ + return texfmt; +} + +int Texture::get_size(int dim) const +{ + if(dim < 0 || dim >= 3) { + return 0; + } + return sz[dim]; +} + +unsigned int Texture::get_id() const +{ + return id; +} + +TextureType Texture::get_type() const +{ + return target_to_type(target); +} + +void Texture::bind(int tex_unit) const +{ + glActiveTexture(GL_TEXTURE0 + tex_unit); + glBindTexture(target, id); + glActiveTexture(GL_TEXTURE0); + + cur_target[tex_unit] = target; +} + + +void Texture::create(int xsz, int ysz, TextureType textype, unsigned int ifmt) +{ + if(textype == TEX_CUBE && xsz != ysz) { + fprintf(stderr, "trying to create cubemap with different width and height (%dx%d)\n", xsz, ysz); + return; + } + + int fmt = glfmt_from_ifmt(ifmt); + int type = gltype_from_ifmt(ifmt); + + target = type_to_target(textype); + + glBindTexture(target, id); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + switch(type) { + case TEX_2D: + glTexImage2D(GL_TEXTURE_2D, 0, glifmt_from_ifmt(ifmt), xsz, ysz, 0, fmt, type, 0); + break; + + case TEX_CUBE: + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + for(int i=0; i<6; i++) { + glTexImage2D(cube_faces[i], 0, ifmt, xsz, ysz, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); + } + break; + } + + sz[0] = xsz; + sz[1] = ysz; + texfmt = ifmt; +} + +#define DEF_IMAGE_SIZE 64 +void Texture::create_default(TextureType type) +{ + if(!default_img) { + default_img = new Image; + default_img->create(DEF_IMAGE_SIZE, DEF_IMAGE_SIZE, Image::FMT_RGBA); + + unsigned char *pixels = (unsigned char*)default_img->get_pixels(); + for(int i=0; i> 3) & 1) == ((j >> 3) & 1); + pixels[0] = chess ? 255 : 32; + pixels[1] = 64; + pixels[2] = chess ? 32 : 255; + pixels[3] = 255; + } + } + } + + switch(type) { + case TEX_2D: + set_image(*default_img); + break; + + case TEX_CUBE: + for(int i=0; i<6; i++) { + set_image(*default_img, i); + } + break; + } +} + +void Texture::set_image(const Image &img, int idx) +{ + if(idx >= 0 && idx < 6) { + set_image_cube(img, idx); + } else { + if(!set_image_cube(img)) { + set_image_2d(img); + } + } +} + +void Texture::set_image_2d(const Image &img) +{ + texfmt = glifmt_from_imgfmt(img.get_format()); + unsigned int fmt = glfmt_from_ifmt(texfmt); + unsigned int type = gltype_from_ifmt(texfmt); + + sz[0] = img.get_width(); + sz[1] = img.get_height(); + + target = GL_TEXTURE_2D; + glBindTexture(target, id); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT); + +#ifdef __GLEW_H__ + if(GLEW_SGIS_generate_mipmap) { + glTexParameteri(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); +#endif + glTexImage2D(target, 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels()); +#ifdef __GLEW_H__ + } else { + gluBuild2DMipmaps(target, texfmt, sz[0], sz[1], fmt, type, img.get_pixels()); + } +#endif + +#ifdef GL_ES_VERSION_2_0 + glGenerateMipmap(target); +#endif +} + +bool Texture::set_image_cube(const Image &img, int idx) +{ + if(idx < 0 || idx >= 6) { + return false; + } + + texfmt = glifmt_from_imgfmt(img.get_format()); + unsigned int fmt = glfmt_from_ifmt(texfmt); + unsigned int type = gltype_from_ifmt(texfmt); + + sz[0] = img.get_width(); + sz[1] = img.get_height(); + + target = GL_TEXTURE_CUBE_MAP; + glBindTexture(target, id); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + glTexImage2D(cube_faces[idx], 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels()); + return true; +} + +bool Texture::set_image_cube(const Image &img) +{ + static const float one_third = 1.0 / 3.0; + static const float two_thirds = 2.0 / 3.0; + static const float hcross[2][6] = { + {0.5, 0.0, 0.25, 0.25, 0.25, 0.75}, {one_third, one_third, 0.0, two_thirds, one_third, one_third} }; + static const float vcross[2][6] = { + {two_thirds, 0.0, one_third, one_third, one_third, one_third}, {0.25, 0.25, 0.0, 0.5, 0.25, 0.75} }; + static const float hsix[2][6] = { + {0.0, 0.0, one_third, one_third, two_thirds, two_thirds}, {0.0, 0.5, 0.0, 0.5, 0.0, 0.5} }; + + int xsz = img.get_width(); + int ysz = img.get_height(); + + if(xsz / 4 == ysz / 3) { + // horizontal cross, assume the vertical bit is center-left + return set_cube_multi(img, hcross[0], hcross[1], xsz / 4); + } + if(xsz / 3 == ysz / 4) { + // vertical cross, assume the horizontal bit is center-top (180-rotated image 5) + return set_cube_multi(img, vcross[0], vcross[1], ysz / 4, (1 << 5)); + } + if(xsz / 3 == ysz / 2) { + // horizontal sixpack + return set_cube_multi(img, hsix[0], hsix[1], ysz / 2); + } + + return false; +} + + +bool Texture::load(const char *fname) +{ + Image img; + if(!img.load(fname)) { + fprintf(stderr, "failed to load 2D texture: %s\n", fname); + return false; + } + set_image(img); + + printf("loaded 2D texture: %s\n", fname); + return true; +} + +bool Texture::load_cube(const char *fname) +{ + Image img; + if(!img.load(fname)) { + return false; + } + return set_image_cube(img); +} + +bool Texture::set_cube_multi(const Image &img, const float *xoffsets, const float *yoffsets, float sz, + unsigned int rotmask) +{ + for(int i=0; i<6; i++) { + Image face; + + int xoffs = xoffsets[i] * img.get_width(); + int yoffs = yoffsets[i] * img.get_height(); + + if(!face.set_pixels(sz, sz, img.get_pixels(), xoffs, yoffs, img.get_width(), img.get_format())) { + return false; + } + + if(rotmask & (1 << i)) { + face.rotate_180(); + } + set_image_cube(face, i); + } + return true; +} + +static int glifmt_from_ifmt(unsigned int ifmt) +{ +#ifdef GL_ES_VERSION_2_0 + switch(ifmt) { + case GL_LUMINANCE16F_ARB: + case GL_LUMINANCE32F_ARB: + ifmt = GL_LUMINANCE; + break; + + case GL_RGB16F: + case GL_RGB32F: + ifmt = GL_RGB; + break; + + case GL_RGBA16F: + case GL_RGBA32F: + ifmt = GL_RGBA; + break; + + default: + break; + } +#endif + return ifmt; // by default just pass it through... +} + +static int glfmt_from_ifmt(unsigned int ifmt) +{ + switch(ifmt) { + case GL_LUMINANCE16F_ARB: + case GL_LUMINANCE32F_ARB: + return GL_LUMINANCE; + + case GL_RGB16F: + case GL_RGB32F: + return GL_RGB; + + case GL_RGBA16F: + case GL_RGBA32F: + return GL_RGBA; + + default: + break; + } + return ifmt; +} + +static int gltype_from_ifmt(unsigned int ifmt) +{ + switch(ifmt) { + case GL_RGB16F: + case GL_RGBA16F: + case GL_LUMINANCE16F_ARB: +#ifdef GL_ES_VERSION_2_0 + return GL_HALF_FLOAT_OES; +#endif + case GL_RGB32F: + case GL_RGBA32F: + case GL_LUMINANCE32F_ARB: + return GL_FLOAT; + + default: + break; + } + return GL_UNSIGNED_BYTE; +} + +static int glifmt_from_imgfmt(Image::Format fmt) +{ + switch(fmt) { + case Image::FMT_GREY: + return GL_LUMINANCE; + case Image::FMT_GREY_FLOAT: + return GL_LUMINANCE16F_ARB; + case Image::FMT_RGB: + return GL_RGB; + case Image::FMT_RGB_FLOAT: + return GL_RGB16F; + case Image::FMT_RGBA: + return GL_RGBA; + case Image::FMT_RGBA_FLOAT: + return GL_RGBA16F; + default: + break; + } + return 0; +} + +static unsigned int type_to_target(TextureType type) +{ + return type == TEX_CUBE ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; +} + +static TextureType target_to_type(unsigned int targ) +{ + return targ == GL_TEXTURE_CUBE_MAP ? TEX_CUBE : TEX_2D; +} + +// ---- TextureSet ---- +TextureSet::TextureSet() + : DataSet(create_tex, load_tex, done_tex, free_tex) +{ +} + +Texture *TextureSet::get_texture(const char *name, TextureType type) const +{ + typename std::map::const_iterator iter = data.find(name); + if(iter != data.end()) { + return iter->second; + } + + Texture *res = create(); + res->create_default(type); + resman_lookup(rman, name, res); + return res; +} + +// static callbacks + +Texture *TextureSet::create_tex() +{ + return new Texture; +} + +bool TextureSet::load_tex(Texture *tex, const char *fname) +{ + Image *img = new Image; + if(!img->load(fname)) { + delete img; + return false; + } + + delete tex->img; + tex->img = img; + + return true; +} + +bool TextureSet::done_tex(Texture *tex) +{ + if(!tex->img) { + return false; + } + + tex->set_image(*tex->img); + return true; +} + +void TextureSet::free_tex(Texture *tex) +{ + delete tex; +} diff --git a/src/texture.h b/src/texture.h new file mode 100644 index 0000000..09b2c91 --- /dev/null +++ b/src/texture.h @@ -0,0 +1,74 @@ +#ifndef TEXTURE_H_ +#define TEXTURE_H_ + +#include "dataset.h" +#include "opengl.h" + +class Image; + +enum TextureType { TEX_2D, TEX_CUBE }; + +class Texture { +private: + unsigned int id; + unsigned int target; + unsigned int texfmt; + int sz[3]; + Image *img; + static Image *default_img; + + Texture(const Texture &tex) {} + Texture &operator =(const Texture &tex) { return *this; } + + void set_image_2d(const Image &img); + bool set_image_cube(const Image &img, int idx); + bool set_image_cube(const Image &img); + + bool load_cube(const char *fname); + + /* for loading multiple cubemap faces from a single image */ + bool set_cube_multi(const Image &img, const float *xoffsets, const float *yoffsets, float sz, + unsigned int rotmask = 0); + +public: + Texture(); + ~Texture(); + + void set_wrapping(unsigned int wrap); + void set_filtering(unsigned int filt); + void set_filtering(unsigned int min_filt, unsigned int mag_filt); + + unsigned int get_format() const; + + int get_size(int dim) const; + + void create(int xsz, int ysz, TextureType type = TEX_2D, unsigned int ifmt = GL_RGBA); + void create_default(TextureType type = TEX_2D); + void set_image(const Image &img, int idx = -1); + + bool load(const char *fname); + + unsigned int get_id() const; + TextureType get_type() const; + + void bind(int tex_unit = 0) const; + + friend class TextureSet; +}; + +void bind_texture(Texture *tex, int tunit = 0); + +class TextureSet : public DataSet { +private: + static Texture *create_tex(); + static bool load_tex(Texture *tex, const char *fname); + static bool done_tex(Texture *tex); + static void free_tex(Texture *tex); + +public: + TextureSet(); + + Texture *get_texture(const char *name, TextureType type = TEX_2D) const; +}; + +#endif // TEXTURE_H_