# HG changeset patch # User John Tsiombikas # Date 1440995917 -10800 # Node ID 87dfe0e10235ed6ee9cd4fb56775f952dda9f319 initial commit diff -r 000000000000 -r 87dfe0e10235 .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,5 @@ +^amiga$ +\.o$ +\.swp$ +\.d$ +^data/ diff -r 000000000000 -r 87dfe0e10235 Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,45 @@ +ccsrc = $(wildcard src/*.cc) +csrc = $(wildcard src/*.c) +obj = $(csrc:.c=.o) $(ccsrc:.cc=.o) +dep = $(obj:.o=.d) + +bin = amiga + +sys = $(shell uname -s) + +warn = -pedantic -Wall -Wno-format-extra-args +dbg = -g +opt = -O0 + +CFLAGS = $(warn) $(dbg) $(opt) +CXXFLAGS = $(CFLAGS) +LDFLAGS = $(libgl) -lvmath -limago -lpsys -lanim -lm + +ifeq ($(sys), Darwin) + libgl = -framework OpenGL -framework GLUT -lGLEW + warn += -Wno-deprecated-declarations +else + libgl = -lGL -lGLU -lglut -lGLEW +endif + +.PHONY: all +all: $(bin) + +$(bin): $(obj) + $(CXX) -o $@ $(obj) $(LDFLAGS) + +-include $(dep) + +%.d: %.c + @$(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@ + +%.d: %.cc + @$(CPP) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@ + +.PHONY: clean +clean: + rm -f $(obj) $(bin) + +.PHONY: cleandep +cleandep: + rm -f $(dep) diff -r 000000000000 -r 87dfe0e10235 sdr/shadow-notex.p.glsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdr/shadow-notex.p.glsl Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,30 @@ +/* vi: set ft=glsl */ +uniform sampler2DShadow shadowmap; + +varying vec3 vdir, ldir, normal; +varying vec4 shadow_tc; + +#define KD gl_FrontMaterial.diffuse.rgb +#define KS gl_FrontMaterial.specular.rgb +#define SPOW gl_FrontMaterial.shininess + +void main() +{ + float shadow = shadow2DProj(shadowmap, shadow_tc).x; + shadow = min(shadow + 1.0 - step(0.0, shadow_tc.w), 1.0); + + vec3 n = normalize(normal); + vec3 v = normalize(vdir); + vec3 l = normalize(ldir); + vec3 h = normalize(l + v); + + float ndotl = max(dot(n, l), 0.0); + float ndoth = max(dot(n, h), 0.0); + + vec3 diffuse = KD * gl_LightSource[0].diffuse.rgb * ndotl; + vec3 specular = KS * gl_LightSource[0].specular.rgb * pow(ndoth, SPOW); + + vec3 ambient = gl_LightModel.ambient.rgb * KD; + gl_FragColor.rgb = ambient + (diffuse + specular) * shadow; + gl_FragColor.a = gl_FrontMaterial.diffuse.a; +} diff -r 000000000000 -r 87dfe0e10235 sdr/shadow.p.glsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdr/shadow.p.glsl Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,32 @@ +/* vi: set ft=glsl */ +uniform sampler2D tex; +uniform sampler2DShadow shadowmap; + +varying vec3 vdir, ldir, normal; +varying vec4 shadow_tc; + +#define KD gl_FrontMaterial.diffuse.rgb +#define KS gl_FrontMaterial.specular.rgb +#define SPOW gl_FrontMaterial.shininess + +void main() +{ + float shadow = shadow2DProj(shadowmap, shadow_tc).x; + vec4 texel = texture2D(tex, gl_TexCoord[0].st); + + vec3 n = normalize(normal); + vec3 v = normalize(vdir); + vec3 l = normalize(ldir); + vec3 h = normalize(l + v); + + float ndotl = max(dot(n, l), 0.0); + float ndoth = max(dot(n, h), 0.0); + + vec3 albedo = KD * texel.rgb; + vec3 diffuse = albedo * gl_LightSource[0].diffuse.rgb * ndotl; + vec3 specular = KS * gl_LightSource[0].specular.rgb * pow(ndoth, SPOW); + + vec3 ambient = gl_LightModel.ambient.rgb * albedo; + gl_FragColor.rgb = ambient + (diffuse + specular) * shadow; + gl_FragColor.a = gl_FrontMaterial.diffuse.a * texel.a; +} diff -r 000000000000 -r 87dfe0e10235 sdr/shadow.v.glsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdr/shadow.v.glsl Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,21 @@ +varying vec3 vdir, ldir, normal; +varying vec4 shadow_tc; + +void main() +{ + gl_Position = ftransform(); + + vec3 vpos = (gl_ModelViewMatrix * gl_Vertex).xyz; + normal = normalize(gl_NormalMatrix * gl_Normal); + vdir = -vpos; + ldir = gl_LightSource[0].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 -r 000000000000 -r 87dfe0e10235 src/game.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/game.cc Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,254 @@ +#include +#include +#include "opengl.h" +#include "game.h" +#include "sdr.h" +#include "shader.h" +#include "shadow.h" +#include "opt.h" +#include "mesh.h" +#include "scene.h" + +static void draw_scene(); + +int win_width, win_height; +unsigned long cur_time; +bool dbg_wireframe; +int dbg_int; + +unsigned int sdr_shadow, sdr_shadow_notex; + +static float cam_theta, cam_phi = 23, cam_dist = 10; +static float cam_x, cam_y, cam_z; +static bool bnstate[8]; +static int prev_x, prev_y; + +static unsigned int modkeys; + +static Scene *scn; + + +bool game_init() +{ + if(init_opengl() == -1) { + return false; + } + + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glEnable(GL_NORMALIZE); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + + float amb[] = {0.1, 0.1, 0.1, 1.0}; + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, amb); + + if(glcaps.sep_spec) { + glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR); + } + glEnable(GL_MULTISAMPLE); + + if(!init_shadow(4096)) { + fprintf(stderr, "failed to initialize shadowmaps\n"); + return false; + } + if(!(sdr_shadow = create_program_load("sdr/shadow.v.glsl", "sdr/shadow.p.glsl"))) { + return false; + } + set_uniform_int(sdr_shadow, "tex", 0); + set_uniform_int(sdr_shadow, "shadowmap", 1); + + 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); + + scn = new Scene; + if(!scn->load("data/amiga.obj")) { + return false; + } + + Mesh::use_custom_sdr_attr = false; + assert(glGetError() == GL_NO_ERROR); + return true; +} + +void game_cleanup() +{ + delete scn; +} + +void game_update(unsigned long time_msec) +{ + cur_time = time_msec; +} + +void game_display() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0.1, -cam_dist); + glRotatef(cam_phi, 1, 0, 0); + glRotatef(cam_theta, 0, 1, 0); + glTranslatef(-cam_x, -cam_y, -cam_z); + + float lpos[] = {-5, 10, 5, 1}; + glLightfv(GL_LIGHT0, GL_POSITION, lpos); + + if(opt.shadows && sdr_shadow) { + begin_shadow_pass(Vector3(lpos[0], lpos[1], lpos[2]), Vector3(0, 0, 0), 45); + draw_scene(); + end_shadow_pass(); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, get_shadow_tex()); + + glMatrixMode(GL_TEXTURE); + Matrix4x4 shadow_matrix = get_shadow_matrix(); + glLoadTransposeMatrixf(shadow_matrix[0]); + + glActiveTexture(GL_TEXTURE0); + glMatrixMode(GL_MODELVIEW); + + override_shader(sdr_shadow_notex); + draw_scene(); + override_shader(0); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + } else { + draw_scene(); + } +} + +static void glmaterial(float r, float g, float b, float spec, float shin) +{ + float color[] = {r, g, b, 1}; + float scolor[] = {spec, spec, spec, 1}; + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, scolor); + glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shin); +} + +static void draw_scene() +{ + glmaterial(0.2, 0.2, 0.4, 0.0, 60.0); + glBegin(GL_QUADS); + glNormal3f(0, 1, 0); + glVertex3f(-5, 0, 5); + glVertex3f(5, 0, 5); + glVertex3f(5, 0, -5); + glVertex3f(-5, 0, -5); + glEnd(); + + glPushMatrix(); + glTranslatef(0, 0.75, 0); + + glmaterial(1.0, 0.4, 0.3, 0.8, 60.0); + draw_teapot(); + + glPopMatrix(); + + scn->draw(); +} + + +void game_reshape(int x, int y) +{ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(45, (float)x / (float)y, 0.2, 200.0); + + glViewport(0, 0, x, y); +} + +void game_keyboard(int bn, bool press) +{ + if(press) { + switch(bn) { + case 27: + quit(); + + case 'd': + scn->dump(stdout); + break; + + case 'w': + dbg_wireframe = !dbg_wireframe; + redisplay(); + break; + + case 's': + opt.shadows = !opt.shadows; + redisplay(); + break; + + case 'p': + printf("camera pos(%g %g %g) rot(%g %g) d(%g)\n", + cam_x, cam_y, cam_z, cam_theta, cam_phi, cam_dist); + break; + } + } +} + +void game_modifier_key(int key, bool press) +{ + if(press) { + modkeys |= (1 << key); + } else { + modkeys &= ~(1 << key); + } +} + +void game_mbutton(int bn, bool press, int x, int y) +{ + bnstate[bn] = press; + prev_x = x; + prev_y = y; + + if(modkeys) { + return; + } + + if(bn == 0) { + } +} + +void game_mmotion(int x, int y) +{ + int dx = x - prev_x; + int dy = y - prev_y; + prev_x = x; + prev_y = y; + + if(modkeys) { + if(bnstate[0]) { + cam_theta += dx * 0.5; + cam_phi += dy * 0.5; + + if(cam_phi < -90) cam_phi = -90; + if(cam_phi > 90) cam_phi = 90; + } + if(bnstate[1]) { + float theta = DEG_TO_RAD(cam_theta); + + float dxp = dx * 0.1; + float dyp = dy * 0.1; + + cam_x += cos(theta) * dxp - sin(theta) * dyp; + if(modkeys & (1 << MOD_SHIFT)) { + cam_y -= sin(theta) * dxp + cos(theta) * dyp; + } else { + cam_z += sin(theta) * dxp + cos(theta) * dyp; + } + } + if(bnstate[2]) { + cam_dist += dy * 0.1; + if(cam_dist < 0.0) cam_dist = 0.0; + } + } +} diff -r 000000000000 -r 87dfe0e10235 src/game.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/game.h Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,30 @@ +#ifndef GAME_H_ +#define GAME_H_ + +#include "vmath/vmath.h" + +extern int win_width, win_height; +extern unsigned long cur_time; + +extern bool dbg_wireframe; +extern int dbg_int; + +enum { MOD_ALT, MOD_CTL, MOD_SHIFT }; + +bool game_init(); +void game_cleanup(); +void game_update(unsigned long time_msec); +void game_display(); +void game_reshape(int x, int y); +void game_keyboard(int key, bool press); +void game_modifier_key(int key, bool press); +void game_mbutton(int bn, bool press, int x, int y); +void game_mmotion(int x, int y); + +void set_fullscreen(bool fs); +void redisplay(); +void quit(); + +void draw_teapot(); + +#endif /* GAME_H_ */ diff -r 000000000000 -r 87dfe0e10235 src/geom.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/geom.cc Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,334 @@ +#include +#include +#include +#include "geom.h" + +GeomObject::~GeomObject() +{ +} + + +Sphere::Sphere() +{ + radius = 1.0; +} + +Sphere::Sphere(const Vector3 ¢, float radius) + : center(cent) +{ + this->radius = radius; +} + +void Sphere::set_union(const GeomObject *obj1, const GeomObject *obj2) +{ + const Sphere *sph1 = dynamic_cast(obj1); + const Sphere *sph2 = dynamic_cast(obj2); + + if(!sph1 || !sph2) { + fprintf(stderr, "Sphere::set_union: arguments must be spheres"); + return; + } + + float dist = (sph1->center - sph2->center).length(); + float surf_dist = dist - (sph1->radius + sph2->radius); + float d1 = sph1->radius + surf_dist / 2.0; + float d2 = sph2->radius + surf_dist / 2.0; + float t = d1 / (d1 + d2); + + if(t < 0.0) t = 0.0; + if(t > 1.0) t = 1.0; + + center = sph1->center * t + sph2->center * (1.0 - t); + radius = std::max(dist * t + sph2->radius, dist * (1.0f - t) + sph1->radius); +} + +void Sphere::set_intersection(const GeomObject *obj1, const GeomObject *obj2) +{ + fprintf(stderr, "Sphere::intersection undefined\n"); +} + +bool Sphere::intersect(const Ray &ray, HitPoint *hit) const +{ + float a = dot_product(ray.dir, ray.dir); + float b = 2.0 * ray.dir.x * (ray.origin.x - center.x) + + 2.0 * ray.dir.y * (ray.origin.y - center.y) + + 2.0 * ray.dir.z * (ray.origin.z - center.z); + float c = dot_product(ray.origin, ray.origin) + dot_product(center, center) - + 2.0 * dot_product(ray.origin, center) - radius * radius; + + float discr = b * b - 4.0 * a * c; + if(discr < 1e-4) { + return false; + } + + float sqrt_discr = sqrt(discr); + float t0 = (-b + sqrt_discr) / (2.0 * a); + float t1 = (-b - sqrt_discr) / (2.0 * a); + + if(t0 < 1e-4) + t0 = t1; + if(t1 < 1e-4) + t1 = t0; + + float t = t0 < t1 ? t0 : t1; + if(t < 1e-4) { + return false; + } + + // fill the HitPoint structure + if(hit) { + hit->obj = this; + hit->dist = t; + hit->pos = ray.origin + ray.dir * t; + hit->normal = (hit->pos - center) / radius; + } + return true; +} + + +AABox::AABox() +{ +} + +AABox::AABox(const Vector3 &vmin, const Vector3 &vmax) + : min(vmin), max(vmax) +{ +} + +void AABox::set_union(const GeomObject *obj1, const GeomObject *obj2) +{ + const AABox *box1 = dynamic_cast(obj1); + const AABox *box2 = dynamic_cast(obj2); + + if(!box1 || !box2) { + fprintf(stderr, "AABox::set_union: arguments must be AABoxes too\n"); + return; + } + + min.x = std::min(box1->min.x, box2->min.x); + min.y = std::min(box1->min.y, box2->min.y); + min.z = std::min(box1->min.z, box2->min.z); + + max.x = std::max(box1->max.x, box2->max.x); + max.y = std::max(box1->max.y, box2->max.y); + max.z = std::max(box1->max.z, box2->max.z); +} + +void AABox::set_intersection(const GeomObject *obj1, const GeomObject *obj2) +{ + const AABox *box1 = dynamic_cast(obj1); + const AABox *box2 = dynamic_cast(obj2); + + if(!box1 || !box2) { + fprintf(stderr, "AABox::set_intersection: arguments must be AABoxes too\n"); + return; + } + + for(int i=0; i<3; i++) { + min[i] = std::max(box1->min[i], box2->min[i]); + max[i] = std::min(box1->max[i], box2->max[i]); + + if(max[i] < min[i]) { + max[i] = min[i]; + } + } +} + +bool AABox::intersect(const Ray &ray, HitPoint *hit) const +{ + Vector3 param[2] = {min, max}; + Vector3 inv_dir(1.0 / ray.dir.x, 1.0 / ray.dir.y, 1.0 / ray.dir.z); + int sign[3] = {inv_dir.x < 0, inv_dir.y < 0, inv_dir.z < 0}; + + float tmin = (param[sign[0]].x - ray.origin.x) * inv_dir.x; + float tmax = (param[1 - sign[0]].x - ray.origin.x) * inv_dir.x; + float tymin = (param[sign[1]].y - ray.origin.y) * inv_dir.y; + float tymax = (param[1 - sign[1]].y - ray.origin.y) * inv_dir.y; + + if(tmin > tymax || tymin > tmax) { + return false; + } + if(tymin > tmin) { + tmin = tymin; + } + if(tymax < tmax) { + tmax = tymax; + } + + float tzmin = (param[sign[2]].z - ray.origin.z) * inv_dir.z; + float tzmax = (param[1 - sign[2]].z - ray.origin.z) * inv_dir.z; + + if(tmin > tzmax || tzmin > tmax) { + return false; + } + if(tzmin > tmin) { + tmin = tzmin; + } + if(tzmax < tmax) { + tmax = tzmax; + } + + float t = tmin < 1e-4 ? tmax : tmin; + if(t >= 1e-4) { + + if(hit) { + hit->obj = this; + hit->dist = t; + hit->pos = ray.origin + ray.dir * t; + + float min_dist = FLT_MAX; + Vector3 offs = min + (max - min) / 2.0; + Vector3 local_hit = hit->pos - offs; + + static const Vector3 axis[] = { + Vector3(1, 0, 0), Vector3(0, 1, 0), Vector3(0, 0, 1) + }; + //int tcidx[][2] = {{2, 1}, {0, 2}, {0, 1}}; + + for(int i=0; i<3; i++) { + float dist = fabs((max[i] - offs[i]) - fabs(local_hit[i])); + if(dist < min_dist) { + min_dist = dist; + hit->normal = axis[i] * (local_hit[i] < 0.0 ? 1.0 : -1.0); + //hit->texcoord = Vector2(hit->pos[tcidx[i][0]], hit->pos[tcidx[i][1]]); + } + } + } + return true; + } + return false; + +} + +Plane::Plane() + : normal(0.0, 1.0, 0.0) +{ +} + +Plane::Plane(const Vector3 &p, const Vector3 &norm) + : pt(p) +{ + normal = norm.normalized(); +} + +Plane::Plane(const Vector3 &p1, const Vector3 &p2, const Vector3 &p3) + : pt(p1) +{ + normal = cross_product(p2 - p1, p3 - p1).normalized(); +} + +Plane::Plane(const Vector3 &normal, float dist) +{ + this->normal = normal.normalized(); + pt = this->normal * dist; +} + +void Plane::set_union(const GeomObject *obj1, const GeomObject *obj2) +{ + fprintf(stderr, "Plane::set_union undefined\n"); +} + +void Plane::set_intersection(const GeomObject *obj1, const GeomObject *obj2) +{ + fprintf(stderr, "Plane::set_intersection undefined\n"); +} + +bool Plane::intersect(const Ray &ray, HitPoint *hit) const +{ + float ndotdir = dot_product(normal, ray.dir); + if(fabs(ndotdir) < 1e-4) { + return false; + } + + if(hit) { + Vector3 ptdir = pt - ray.origin; + float t = dot_product(normal, ptdir) / ndotdir; + + hit->pos = ray.origin + ray.dir * t; + hit->normal = normal; + hit->obj = this; + } + return true; +} + +float sphere_distance(const Vector3 ¢, float rad, const Vector3 &pt) +{ + return (pt - cent).length() - rad; +} + +// TODO version which takes both radii into account +float capsule_distance(const Vector3 &a, float ra, const Vector3 &b, float rb, const Vector3 &pt) +{ + Vector3 ab_dir = b - a; + float ab_len_sq = ab_dir.length_sq(); + + if(fabs(ab_len_sq) < 1e-5) { + // if a == b, the capsule is a sphere with radius the maximum of the capsule radii + return sphere_distance(a, std::max(ra, rb), pt); + } + float ab_len = sqrt(ab_len_sq); + + Vector3 ap_dir = pt - a; + + float t = dot_product(ap_dir, ab_dir / ab_len) / ab_len; + if(t < 0.0) { + return sphere_distance(a, ra, pt); + } + if(t >= 1.0) { + return sphere_distance(b, rb, pt); + } + + Vector3 pproj = a + ab_dir * t; + return (pproj - pt).length() - ra; +} + +#if 0 +float capsule_distance(const Vector3 &a, float ra, const Vector3 &b, float rb, const Vector3 &pt) +{ + Vector3 ab_dir = b - a; + + if(fabs(ab_dir.length_sq()) < 1e-5) { + // if a == b, the capsule is a sphere with radius the maximum of the capsule radii + return sphere_distance(a, std::max(ra, rb), pt); + } + float ab_len = ab_dir.length(); + + Vector3 ap_dir = pt - a; + Vector3 rotaxis = cross_product(ab_dir, ap_dir).normalized(); + + Matrix4x4 rmat; + rmat.set_rotation(rotaxis, M_PI / 2.0); + Vector3 right = ab_dir.transformed(rmat) / ab_len; + + // XXX I think this check is redundant, always false, due to the cross product order + //assert(dot_product(right, ab_dir) >= 0.0); + if(dot_product(right, ab_dir) < 0.0) { + right = -right; + } + Vector3 aa = a + right * ra; + Vector3 bb = b + right * rb; + + // project pt to the line segment bb-aa, see if the projection lies within the interval [0, 1) + Vector3 aabb_dir = bb - aa; + float aabb_len = aabb_dir.length(); + Vector3 aap_dir = pt - aa; + + float t = dot_product(aap_dir, aabb_dir / aabb_len) / aabb_len; + if(t < 0.0) { + return sphere_distance(a, ra, pt); + } + if(t >= 1.0) { + return sphere_distance(b, rb, pt); + } + + Vector3 ppt = aa + aabb_dir * t; + Vector3 norm = ppt - pt; + float dist = norm.length(); + + if(dot_product(norm, right) < 0.0) { + // inside the cone + dist = -dist; + } + return dist; +} +#endif diff -r 000000000000 -r 87dfe0e10235 src/geom.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/geom.h Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,73 @@ +#ifndef GEOMOBJ_H_ +#define GEOMOBJ_H_ + +#include "vmath/vmath.h" + +class GeomObject; +class SceneNode; + +struct HitPoint { + float dist; //< parametric distance along the ray + Vector3 pos; //< position of intersection (orig + dir * dist) + Vector3 normal; //< normal at the point of intersection + const void *obj; //< pointer to the intersected object + const SceneNode *node; + Ray ray; +}; + +class GeomObject { +public: + virtual ~GeomObject(); + + virtual void set_union(const GeomObject *obj1, const GeomObject *obj2) = 0; + virtual void set_intersection(const GeomObject *obj1, const GeomObject *obj2) = 0; + + virtual bool intersect(const Ray &ray, HitPoint *hit = 0) const = 0; +}; + +class Sphere : public GeomObject { +public: + Vector3 center; + float radius; + + Sphere(); + Sphere(const Vector3 ¢er, float radius); + + void set_union(const GeomObject *obj1, const GeomObject *obj2); + void set_intersection(const GeomObject *obj1, const GeomObject *obj2); + + bool intersect(const Ray &ray, HitPoint *hit = 0) const; +}; + +class AABox : public GeomObject { +public: + Vector3 min, max; + + AABox(); + AABox(const Vector3 &min, const Vector3 &max); + + void set_union(const GeomObject *obj1, const GeomObject *obj2); + void set_intersection(const GeomObject *obj1, const GeomObject *obj2); + + bool intersect(const Ray &ray, HitPoint *hit = 0) const; +}; + +class Plane : public GeomObject { +public: + Vector3 pt, normal; + + Plane(); + Plane(const Vector3 &pt, const Vector3 &normal); + Plane(const Vector3 &p1, const Vector3 &p2, const Vector3 &p3); + Plane(const Vector3 &normal, float dist); + + void set_union(const GeomObject *obj1, const GeomObject *obj2); + void set_intersection(const GeomObject *obj1, const GeomObject *obj2); + + bool intersect(const Ray &ray, HitPoint *hit = 0) const; +}; + +float sphere_distance(const Vector3 ¢, float rad, const Vector3 &pt); +float capsule_distance(const Vector3 &a, float ra, const Vector3 &b, float rb, const Vector3 &pt); + +#endif // GEOMOBJ_H_ diff -r 000000000000 -r 87dfe0e10235 src/light.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/light.cc Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,17 @@ +#include "light.h" +#include "opengl.h" + +Light::Light() + : color(1, 1, 1) +{ +} + +void Light::setup(int idx) const +{ + float lpos[] = {pos.x, pos.y, pos.z, 1.0}; + float col[] = {color.x, color.y, color.z, 1.0}; + + glLightfv(GL_LIGHT0 + idx, GL_POSITION, lpos); + glLightfv(GL_LIGHT0 + idx, GL_DIFFUSE, col); + glLightfv(GL_LIGHT0 + idx, GL_SPECULAR, col); +} diff -r 000000000000 -r 87dfe0e10235 src/light.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/light.h Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,16 @@ +#ifndef LIGHT_H_ +#define LIGHT_H_ + +#include "vmath/vmath.h" + +class Light { +public: + Vector3 pos; + Vector3 color; + + Light(); + + void setup(int idx = 0) const; +}; + +#endif // LIGHT_H_ diff -r 000000000000 -r 87dfe0e10235 src/main.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main.cc Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,148 @@ +#include +#include +#include +#ifdef __APPLE__ +#include +#else +#include +#endif +#include "game.h" +#include "opt.h" + +static void display(); +static void idle(); +static void reshape(int x, int y); +static void keydown(unsigned char key, int x, int y); +static void keyup(unsigned char key, int x, int y); +static void mouse(int bn, int st, int x, int y); +static void motion(int x, int y); +static void update_modifiers(); + +static unsigned int start_time; + +int main(int argc, char **argv) +{ + glutInit(&argc, argv); + if(!init_options(argc, argv)) { + return 1; + } + glutInitWindowSize(opt.xres, opt.yres); + glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE | GLUT_MULTISAMPLE); + glutCreateWindow("umonster"); + + if(opt.fullscreen) { + set_fullscreen(true); + } + + glutDisplayFunc(display); + glutIdleFunc(idle); + glutReshapeFunc(reshape); + glutKeyboardFunc(keydown); + glutKeyboardUpFunc(keyup); + glutMouseFunc(mouse); + glutMotionFunc(motion); + glutPassiveMotionFunc(motion); + + if(!game_init()) { + return 1; + } + atexit(game_cleanup); + + start_time = glutGet(GLUT_ELAPSED_TIME); + glutMainLoop(); + return 0; +} + +void set_fullscreen(bool fs) +{ + if(fs) { + glutFullScreen(); + } else { + glutReshapeWindow(opt.xres, opt.yres); + } +} + +void redisplay() +{ + glutPostRedisplay(); +} + +void quit() +{ + exit(0); +} + +void draw_teapot() +{ + glFrontFace(GL_CW); + glutSolidTeapot(1.0); + glFrontFace(GL_CCW); +} + +static void display() +{ + unsigned int msec = glutGet(GLUT_ELAPSED_TIME) - start_time; + game_update(msec); + + game_display(); + + glutSwapBuffers(); + assert(glGetError() == GL_NO_ERROR); +} + +static void idle() +{ + glutPostRedisplay(); +} + +static void reshape(int x, int y) +{ + win_width = x; + win_height = y; + game_reshape(x, y); +} + +static void keydown(unsigned char key, int x, int y) +{ + update_modifiers(); + game_keyboard(key, true); +} + +static void keyup(unsigned char key, int x, int y) +{ + update_modifiers(); + game_keyboard(key, false); +} + +static void mouse(int bn, int st, int x, int y) +{ + update_modifiers(); + game_mbutton(bn - GLUT_LEFT_BUTTON, st == GLUT_DOWN, x, y); +} + +static void motion(int x, int y) +{ + game_mmotion(x, y); +} + +static void update_modifiers() +{ + static unsigned int prev_mod; + unsigned int mod = glutGetModifiers(); + unsigned int delta = mod ^ prev_mod; + + if(delta & GLUT_ACTIVE_SHIFT) { + bool press = (mod & GLUT_ACTIVE_SHIFT) != 0; + game_modifier_key(MOD_SHIFT, press); + } + if(delta & GLUT_ACTIVE_CTRL) { + bool press = (mod & GLUT_ACTIVE_CTRL) != 0; + game_modifier_key(MOD_CTL, press); + } + if(delta & GLUT_ACTIVE_ALT) { + bool press = (mod & GLUT_ACTIVE_ALT) != 0; + game_modifier_key(MOD_ALT, press); + } + + prev_mod = mod; +} diff -r 000000000000 -r 87dfe0e10235 src/mesh.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mesh.cc Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,1297 @@ +#include +#include +#include +#include +#include "opengl.h" +#include "mesh.h" +//#include "xform_node.h" + +#define USE_OLDGL + +bool Mesh::use_custom_sdr_attr = true; +int Mesh::global_sdr_loc[NUM_MESH_ATTR] = { 0, 1, 2, 3, 4, 5, 6 }; +/* + (int)SDR_ATTR_VERTEX, + (int)SDR_ATTR_NORMAL, + (int)SDR_ATTR_TANGENT, + (int)SDR_ATTR_TEXCOORD, + (int)SDR_ATTR_COLOR, + -1, -1}; +*/ +unsigned int Mesh::intersect_mode = ISECT_DEFAULT; +float Mesh::vertex_sel_dist = 0.01; +float Mesh::vis_vecsize = 1.0; + +Mesh::Mesh() +{ + clear(); + + glGenBuffers(NUM_MESH_ATTR + 1, buffer_objects); + + for(int i=0; iname = name; +} + +const char *Mesh::get_name() const +{ + return name.c_str(); +} + +bool Mesh::has_attrib(int attr) const +{ + if(attr < 0 || attr >= NUM_MESH_ATTR) { + return false; + } + + // if neither of these is valid, then nobody has set this attribute + return vattr[attr].vbo_valid || vattr[attr].data_valid; +} + +bool Mesh::is_indexed() const +{ + return ibo_valid || idata_valid; +} + +void Mesh::clear() +{ + //bones.clear(); + + for(int i=0; i= NUM_MESH_ATTR) { + fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib); + return 0; + } + + if(nverts && num != nverts) { + fprintf(stderr, "%s: attribute count missmatch (%d instead of %d)\n", __FUNCTION__, num, nverts); + return 0; + } + nverts = num; + + vattr[attrib].data.clear(); + vattr[attrib].nelem = nelem; + vattr[attrib].data.resize(num * nelem); + + if(data) { + memcpy(&vattr[attrib].data[0], data, num * nelem * sizeof *data); + } + + vattr[attrib].data_valid = true; + vattr[attrib].vbo_valid = false; + return &vattr[attrib].data[0]; +} + +float *Mesh::get_attrib_data(int attrib) +{ + if(attrib < 0 || attrib >= NUM_MESH_ATTR) { + fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib); + return 0; + } + + vattr[attrib].vbo_valid = false; + return (float*)((const Mesh*)this)->get_attrib_data(attrib); +} + +const float *Mesh::get_attrib_data(int attrib) const +{ + if(attrib < 0 || attrib >= NUM_MESH_ATTR) { + fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib); + return 0; + } + + if(!vattr[attrib].data_valid) { +#if GL_ES_VERSION_2_0 + fprintf(stderr, "%s: can't read back attrib data on CrippledGL ES\n", __FUNCTION__); + return 0; +#else + if(!vattr[attrib].vbo_valid) { + fprintf(stderr, "%s: unavailable attrib: %d\n", __FUNCTION__, attrib); + return 0; + } + + // local data copy is unavailable, grab the data from the vbo + Mesh *m = (Mesh*)this; + m->vattr[attrib].data.resize(nverts * vattr[attrib].nelem); + + glBindBuffer(GL_ARRAY_BUFFER, vattr[attrib].vbo); + void *data = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY); + memcpy(&m->vattr[attrib].data[0], data, nverts * vattr[attrib].nelem * sizeof(float)); + glUnmapBuffer(GL_ARRAY_BUFFER); + + vattr[attrib].data_valid = true; +#endif + } + + return &vattr[attrib].data[0]; +} + +void Mesh::set_attrib(int attrib, int idx, const Vector4 &v) +{ + float *data = get_attrib_data(attrib); + if(data) { + data += idx * vattr[attrib].nelem; + for(int i=0; iget_index_data(); +} + +const unsigned int *Mesh::get_index_data() const +{ + if(!idata_valid) { +#if GL_ES_VERSION_2_0 + fprintf(stderr, "%s: can't read back index data in CrippledGL ES\n", __FUNCTION__); + return 0; +#else + if(!ibo_valid) { + fprintf(stderr, "%s: indices unavailable\n", __FUNCTION__); + return 0; + } + + // local data copy is unavailable, gram the data from the ibo + Mesh *m = (Mesh*)this; + int nidx = nfaces * 3; + m->idata.resize(nidx); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + void *data = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY); + memcpy(&m->idata[0], data, nidx * sizeof(unsigned int)); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + idata_valid = true; +#endif + } + + return &idata[0]; +} + +int Mesh::get_index_count() const +{ + return nfaces * 3; +} + +void Mesh::append(const Mesh &mesh) +{ + unsigned int idxoffs = nverts; + + if(!nverts) { + clone(mesh); + return; + } + + nverts += mesh.nverts; + nfaces += mesh.nfaces; + + for(int i=0; i= NUM_MESH_ATTR) { + return; + } + Mesh::global_sdr_loc[attr] = loc; +} + +/// static function +int Mesh::get_attrib_location(int attr) +{ + if(attr < 0 || attr >= NUM_MESH_ATTR) { + return -1; + } + return Mesh::global_sdr_loc[attr]; +} + +/// static function +void Mesh::clear_attrib_locations() +{ + for(int i=0; i= (int)bones.size()) { + return 0; + } + return bones[idx]; +} + +int Mesh::get_bones_count() const +{ + return (int)bones.size(); +} +*/ + +bool Mesh::pre_draw() const +{ + cur_sdr = 0; + if(glcaps.shaders) { + glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr); + } + + ((Mesh*)this)->update_buffers(); + + if(!vattr[MESH_ATTR_VERTEX].vbo_valid) { + fprintf(stderr, "%s: invalid vertex buffer\n", __FUNCTION__); + return false; + } + + if(cur_sdr && use_custom_sdr_attr) { + // rendering with shaders + if(global_sdr_loc[MESH_ATTR_VERTEX] == -1) { + fprintf(stderr, "%s: shader attribute location for vertices unset\n", __FUNCTION__); + return false; + } + + for(int i=0; i= 0 && vattr[i].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo); + glVertexAttribPointer(loc, vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(loc); + } + } + } else { +#ifndef GL_ES_VERSION_2_0 + // rendering with fixed-function (not available in GLES2) + glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_VERTEX].vbo); + glVertexPointer(vattr[MESH_ATTR_VERTEX].nelem, GL_FLOAT, 0, 0); + glEnableClientState(GL_VERTEX_ARRAY); + + if(vattr[MESH_ATTR_NORMAL].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_NORMAL].vbo); + glNormalPointer(GL_FLOAT, 0, 0); + glEnableClientState(GL_NORMAL_ARRAY); + } + if(vattr[MESH_ATTR_TEXCOORD].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_TEXCOORD].vbo); + glTexCoordPointer(vattr[MESH_ATTR_TEXCOORD].nelem, GL_FLOAT, 0, 0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + } + if(vattr[MESH_ATTR_COLOR].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_COLOR].vbo); + glColorPointer(vattr[MESH_ATTR_COLOR].nelem, GL_FLOAT, 0, 0); + glEnableClientState(GL_COLOR_ARRAY); + } +#endif + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + + return true; +} + +void Mesh::draw() const +{ + if(!pre_draw()) return; + + if(ibo_valid) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + glDrawElements(GL_TRIANGLES, nfaces * 3, GL_UNSIGNED_INT, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } else { + glDrawArrays(GL_TRIANGLES, 0, nverts); + } + + post_draw(); +} + +void Mesh::post_draw() const +{ + if(cur_sdr && use_custom_sdr_attr) { + // rendered with shaders + for(int i=0; i= 0 && vattr[i].vbo_valid) { + glDisableVertexAttribArray(loc); + } + } + } else { +#ifndef GL_ES_VERSION_2_0 + // rendered with fixed-function + glDisableClientState(GL_VERTEX_ARRAY); + if(vattr[MESH_ATTR_NORMAL].vbo_valid) { + glDisableClientState(GL_NORMAL_ARRAY); + } + if(vattr[MESH_ATTR_TEXCOORD].vbo_valid) { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + if(vattr[MESH_ATTR_COLOR].vbo_valid) { + glDisableClientState(GL_COLOR_ARRAY); + } +#endif + } +} + +void Mesh::draw_wire() const +{ + if(!pre_draw()) return; + + ((Mesh*)this)->update_wire_ibo(); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wire_ibo); + glDrawElements(GL_LINES, nfaces * 6, GL_UNSIGNED_INT, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + post_draw(); +} + +void Mesh::draw_vertices() const +{ + if(!pre_draw()) return; + + glDrawArrays(GL_POINTS, 0, nverts); + + post_draw(); +} + +void Mesh::draw_normals() const +{ +#ifdef USE_OLDGL + int cur_sdr = 0; + if(glcaps.shaders) { + glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr); + } + + Vector3 *varr = (Vector3*)get_attrib_data(MESH_ATTR_VERTEX); + Vector3 *norm = (Vector3*)get_attrib_data(MESH_ATTR_NORMAL); + if(!varr || !norm) { + return; + } + + glBegin(GL_LINES); + if(cur_sdr && use_custom_sdr_attr) { + int vert_loc = global_sdr_loc[MESH_ATTR_VERTEX]; + if(vert_loc < 0) { + glEnd(); + return; + } + + for(size_t i=0; icalc_aabb(); + } + *vmin = aabb.min; + *vmax = aabb.max; +} + +const AABox &Mesh::get_aabbox() const +{ + if(!aabb_valid) { + ((Mesh*)this)->calc_aabb(); + } + return aabb; +} + +float Mesh::get_bsphere(Vector3 *center, float *rad) const +{ + if(!bsph_valid) { + ((Mesh*)this)->calc_bsph(); + } + *center = bsph.center; + *rad = bsph.radius; + return bsph.radius; +} + +const Sphere &Mesh::get_bsphere() const +{ + if(!bsph_valid) { + ((Mesh*)this)->calc_bsph(); + } + return bsph; +} + +/// static function +void Mesh::set_intersect_mode(unsigned int mode) +{ + Mesh::intersect_mode = mode; +} + +/// static function +unsigned int Mesh::get_intersect_mode() +{ + return Mesh::intersect_mode; +} + +/// static function +void Mesh::set_vertex_select_distance(float dist) +{ + Mesh::vertex_sel_dist = dist; +} + +/// static function +float Mesh::get_vertex_select_distance() +{ + return Mesh::vertex_sel_dist; +} + +bool Mesh::intersect(const Ray &ray, HitPoint *hit) const +{ + assert((Mesh::intersect_mode & (ISECT_VERTICES | ISECT_FACE)) != (ISECT_VERTICES | ISECT_FACE)); + + const Vector3 *varr = (Vector3*)get_attrib_data(MESH_ATTR_VERTEX); + const Vector3 *narr = (Vector3*)get_attrib_data(MESH_ATTR_NORMAL); + if(!varr) { + return false; + } + const unsigned int *idxarr = get_index_data(); + + // first test with the bounding box + AABox box; + get_aabbox(&box.min, &box.max); + if(!box.intersect(ray)) { + return false; + } + + HitPoint nearest_hit; + nearest_hit.dist = FLT_MAX; + nearest_hit.obj = 0; + + if(Mesh::intersect_mode & ISECT_VERTICES) { + // we asked for "intersections" with the vertices of the mesh + long nearest_vidx = -1; + float thres_sq = Mesh::vertex_sel_dist * Mesh::vertex_sel_dist; + + for(unsigned int i=0; i 0) { + continue; + } + + // project the vertex onto the ray line + float t = dot_product(varr[i] - ray.origin, ray.dir); + Vector3 vproj = ray.origin + ray.dir * t; + + float dist_sq = (vproj - varr[i]).length_sq(); + if(dist_sq < thres_sq) { + if(!hit) { + return true; + } + if(t < nearest_hit.dist) { + nearest_hit.dist = t; + nearest_vidx = i; + } + } + } + + if(nearest_vidx != -1) { + hitvert = varr[nearest_vidx]; + nearest_hit.obj = &hitvert; + } + + } else { + // regular intersection test with polygons + + for(unsigned int i=0; i 0) { + continue; + } + + HitPoint fhit; + if(face.intersect(ray, hit ? &fhit : 0)) { + if(!hit) { + return true; + } + if(fhit.dist < nearest_hit.dist) { + nearest_hit = fhit; + hitface = face; + } + } + } + } + + if(nearest_hit.obj) { + if(hit) { + *hit = nearest_hit; + + // if we are interested in the mesh and not the faces set obj to this + if(Mesh::intersect_mode & ISECT_FACE) { + hit->obj = &hitface; + } else if(Mesh::intersect_mode & ISECT_VERTICES) { + hit->obj = &hitvert; + } else { + hit->obj = this; + } + } + return true; + } + return false; +} + + +// texture coordinate manipulation +void Mesh::texcoord_apply_xform(const Matrix4x4 &xform) +{ + if(!has_attrib(MESH_ATTR_TEXCOORD)) { + return; + } + + for(unsigned int i=0; i abs_ny && abs_nx > abs_nz ? 0 : (abs_ny > abs_nz ? 1 : 2); + + float uv[2], *uvptr = uv; + for(int j=0; j<3; j++) { + if(j == dom) continue; // skip dominant axis + + *uvptr++ = pos[j]; + } + set_attrib(MESH_ATTR_TEXCOORD, i, Vector4(uv[0], uv[1], 0, 1)); + } +} + +void Mesh::texcoord_gen_cylinder() +{ + if(!nverts) return; + + if(!has_attrib(MESH_ATTR_TEXCOORD)) { + // allocate texture coordinate attribute array + set_attrib_data(MESH_ATTR_TEXCOORD, 2, nverts); + } + + for(unsigned int i=0; iget_attrib_data(MESH_ATTR_VERTEX)) { + return; + } + + aabb.min = Vector3(FLT_MAX, FLT_MAX, FLT_MAX); + aabb.max = -aabb.min; + + for(unsigned int i=0; i aabb.max[j]) { + aabb.max[j] = v[j]; + } + } + } + aabb_valid = true; +} + +void Mesh::calc_bsph() +{ + // the cast is to force calling the const version which doesn't invalidate + if(!((const Mesh*)this)->get_attrib_data(MESH_ATTR_VERTEX)) { + return; + } + + Vector3 v; + bsph.center = Vector3(0, 0, 0); + + // first find the center + for(unsigned int i=0; i bsph.radius) { + bsph.radius = dist_sq; + } + } + bsph.radius = sqrt(bsph.radius); + + bsph_valid = true; +} + +void Mesh::update_buffers() +{ + for(int i=0; iget_index_data(); + + for(unsigned int i=0; icalc_normal(); + } + return normal; +} + +void Triangle::transform(const Matrix4x4 &xform) +{ + v[0].transform(xform); + v[1].transform(xform); + v[2].transform(xform); + normal_valid = false; +} + +void Triangle::draw() const +{ + Vector3 n[3]; + n[0] = get_normal(); + n[1] = get_normal(); + n[2] = get_normal(); + + int vloc = Mesh::get_attrib_location(MESH_ATTR_VERTEX); + int nloc = Mesh::get_attrib_location(MESH_ATTR_NORMAL); + + glEnableVertexAttribArray(vloc); + glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, &v[0].x); + glVertexAttribPointer(nloc, 3, GL_FLOAT, GL_FALSE, 0, &n[0].x); + + glDrawArrays(GL_TRIANGLES, 0, 3); + + glDisableVertexAttribArray(vloc); + glDisableVertexAttribArray(nloc); +} + +void Triangle::draw_wire() const +{ + static const int idxarr[] = {0, 1, 1, 2, 2, 0}; + int vloc = Mesh::get_attrib_location(MESH_ATTR_VERTEX); + + glEnableVertexAttribArray(vloc); + glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, &v[0].x); + + glDrawElements(GL_LINES, 6, GL_UNSIGNED_INT, idxarr); + + glDisableVertexAttribArray(vloc); +} + +Vector3 Triangle::calc_barycentric(const Vector3 &pos) const +{ + Vector3 norm = get_normal(); + + float area_sq = fabs(dot_product(cross_product(v[1] - v[0], v[2] - v[0]), norm)); + if(area_sq < 1e-5) { + return Vector3(0, 0, 0); + } + + float asq0 = fabs(dot_product(cross_product(v[1] - pos, v[2] - pos), norm)); + float asq1 = fabs(dot_product(cross_product(v[2] - pos, v[0] - pos), norm)); + float asq2 = fabs(dot_product(cross_product(v[0] - pos, v[1] - pos), norm)); + + return Vector3(asq0 / area_sq, asq1 / area_sq, asq2 / area_sq); +} + +bool Triangle::intersect(const Ray &ray, HitPoint *hit) const +{ + Vector3 normal = get_normal(); + + float ndotdir = dot_product(ray.dir, normal); + if(fabs(ndotdir) < 1e-4) { + return false; + } + + Vector3 vertdir = v[0] - ray.origin; + float t = dot_product(normal, vertdir) / ndotdir; + + Vector3 pos = ray.origin + ray.dir * t; + Vector3 bary = calc_barycentric(pos); + + if(bary.x + bary.y + bary.z > 1.00001) { + return false; + } + + if(hit) { + hit->dist = t; + hit->pos = ray.origin + ray.dir * t; + hit->normal = normal; + hit->obj = this; + } + return true; +} diff -r 000000000000 -r 87dfe0e10235 src/mesh.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mesh.h Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,240 @@ +#ifndef MESH_H_ +#define MESH_H_ + +#include +#include +#include +#include "vmath/vmath.h" +#include "geom.h" + +enum { + MESH_ATTR_VERTEX, + MESH_ATTR_NORMAL, + MESH_ATTR_TANGENT, + MESH_ATTR_TEXCOORD, + MESH_ATTR_COLOR, + MESH_ATTR_BONEWEIGHTS, + MESH_ATTR_BONEIDX, + + NUM_MESH_ATTR +}; + +// intersection mode flags +enum { + ISECT_DEFAULT = 0, // default (whole mesh, all intersections) + ISECT_FRONT = 1, // front-faces only + ISECT_FACE = 2, // return intersected face pointer instead of mesh + ISECT_VERTICES = 4 // return (?) TODO +}; + +//class XFormNode; + + +class Triangle { +public: + Vector3 v[3]; + Vector3 normal; + bool normal_valid; + int id; + + Triangle(); + Triangle(const Vector3 &v0, const Vector3 &v1, const Vector3 &v2); + Triangle(int n, const Vector3 *varr, const unsigned int *idxarr = 0); + + /// calculate normal (quite expensive) + void calc_normal(); + const Vector3 &get_normal() const; + + void transform(const Matrix4x4 &xform); + + void draw() const; + void draw_wire() const; + + /// calculate barycentric coordinates of a point + Vector3 calc_barycentric(const Vector3 &pos) const; + + bool intersect(const Ray &ray, HitPoint *hit = 0) const; +}; + + +class Mesh { +private: + std::string name; + unsigned int nverts, nfaces; + + // current value for each attribute for the immedate mode + // interface. + Vector4 cur_val[NUM_MESH_ATTR]; + + unsigned int buffer_objects[NUM_MESH_ATTR + 1]; + + // vertex attribute data and buffer objects + struct { + int nelem; // number of elements per attribute range: [1, 4] + std::vector data; + unsigned int vbo; + mutable bool vbo_valid; // if this is false, the vbo needs updating from the data + mutable bool data_valid; // if this is false, the data needs to be pulled from the vbo + //int sdr_loc; + } vattr[NUM_MESH_ATTR]; + + static int global_sdr_loc[NUM_MESH_ATTR]; + + //std::vector bones; // bones affecting this mesh + + // index data and buffer object + std::vector idata; + unsigned int ibo; + mutable bool ibo_valid; + mutable bool idata_valid; + + // index buffer object for wireframe rendering (constructed on demand) + unsigned int wire_ibo; + mutable bool wire_ibo_valid; + + // axis-aligned bounding box + mutable AABox aabb; + mutable bool aabb_valid; + + // bounding sphere + mutable Sphere bsph; + mutable bool bsph_valid; + + // keeps the last intersected face + mutable Triangle hitface; + // keeps the last intersected vertex position + mutable Vector3 hitvert; + + void calc_aabb(); + void calc_bsph(); + + static unsigned int intersect_mode; + static float vertex_sel_dist; + + static float vis_vecsize; + + /// update the VBOs after data has changed (invalid vbo/ibo) + void update_buffers(); + /// construct/update the wireframe index buffer (called from draw_wire). + void update_wire_ibo(); + + mutable int cur_sdr; + bool pre_draw() const; + void post_draw() const; + + +public: + static bool use_custom_sdr_attr; + + Mesh(); + ~Mesh(); + + Mesh(const Mesh &rhs); + Mesh &operator =(const Mesh &rhs); + bool clone(const Mesh &m); + + void set_name(const char *name); + const char *get_name() const; + + bool has_attrib(int attr) const; + bool is_indexed() const; + + // clears everything about this mesh, and returns to the newly constructed state + void clear(); + + // access the vertex attribute data + // if vdata == 0, space is just allocated + float *set_attrib_data(int attrib, int nelem, unsigned int num, const float *vdata = 0); // invalidates vbo + float *get_attrib_data(int attrib); // invalidates vbo + const float *get_attrib_data(int attrib) const; + + // simple access to any particular attribute + void set_attrib(int attrib, int idx, const Vector4 &v); // invalidates vbo + Vector4 get_attrib(int attrib, int idx) const; + + int get_attrib_count(int attrib) const; + + // ... same for index data + unsigned int *set_index_data(int num, const unsigned int *indices = 0); // invalidates ibo + unsigned int *get_index_data(); // invalidates ibo + const unsigned int *get_index_data() const; + + int get_index_count() const; + + void append(const Mesh &mesh); + + // immediate-mode style mesh construction interface + void vertex(float x, float y, float z); + void normal(float nx, float ny, float nz); + void tangent(float tx, float ty, float tz); + void texcoord(float u, float v, float w); + void boneweights(float w1, float w2, float w3, float w4); + void boneidx(int idx1, int idx2, int idx3, int idx4); + + int get_poly_count() const; + + /* apply a transformation to the vertices and its inverse-transpose + * to the normals and tangents. + */ + void apply_xform(const Matrix4x4 &xform); + void apply_xform(const Matrix4x4 &xform, const Matrix4x4 &dir_xform); + + void flip(); // both faces and normals + void flip_faces(); + void flip_normals(); + + // adds a bone and returns its index + /*int add_bone(XFormNode *bone); + const XFormNode *get_bone(int idx) const; + int get_bones_count() const;*/ + + // access the shader attribute locations + static void set_attrib_location(int attr, int loc); + static int get_attrib_location(int attr); + static void clear_attrib_locations(); + + static void set_vis_vecsize(float sz); + static float get_vis_vecsize(); + + void draw() const; + void draw_wire() const; + void draw_vertices() const; + void draw_normals() const; + void draw_tangents() const; + + /** get the bounding box in local space. The result will be cached, and subsequent + * calls will return the same box. The cache gets invalidated by any functions that can affect + * the vertex data (non-const variant of get_attrib_data(MESH_ATTR_VERTEX, ...) included). + * @{ */ + void get_aabbox(Vector3 *vmin, Vector3 *vmax) const; + const AABox &get_aabbox() const; + /// @} + + /** get the bounding sphere in local space. The result will be cached, and subsequent + * calls will return the same box. The cache gets invalidated by any functions that can affect + * the vertex data (non-const variant of get_attrib_data(MESH_ATTR_VERTEX, ...) included). + * @{ */ + float get_bsphere(Vector3 *center, float *rad) const; + const Sphere &get_bsphere() const; + + static void set_intersect_mode(unsigned int mode); + static unsigned int get_intersect_mode(); + static void set_vertex_select_distance(float dist); + static float get_vertex_select_distance(); + + /** Find the intersection between the mesh and a ray. + * XXX Brute force at the moment, not intended to be used for anything other than picking in tools. + * If you intend to use it in a speed-critical part of the code, you'll *have* to optimize it! + */ + bool intersect(const Ray &ray, HitPoint *hit = 0) const; + + // texture coordinate manipulation + void texcoord_apply_xform(const Matrix4x4 &xform); + void texcoord_gen_plane(const Vector3 &norm, const Vector3 &tang); + void texcoord_gen_box(); + void texcoord_gen_cylinder(); + + void dump(FILE *fp) const; +}; + +#endif // MESH_H_ diff -r 000000000000 -r 87dfe0e10235 src/meshgen.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/meshgen.cc Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,775 @@ +#include +#include "meshgen.h" +#include "mesh.h" + +// -------- sphere -------- + +#define SURAD(u) ((u) * 2.0 * M_PI) +#define SVRAD(v) ((v) * M_PI) + +static Vector3 sphvec(float theta, float phi) +{ + return Vector3(sin(theta) * sin(phi), + cos(phi), + cos(theta) * sin(phi)); +} + +void gen_sphere(Mesh *mesh, float rad, int usub, int vsub, float urange, float vrange) +{ + if(usub < 4) usub = 4; + if(vsub < 2) vsub = 2; + + int uverts = usub + 1; + int vverts = vsub + 1; + + int num_verts = uverts * vverts; + int num_quads = usub * vsub; + int num_tri = num_quads * 2; + + mesh->clear(); + Vector3 *varr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vector3 *narr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vector3 *tarr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vector2 *uvarr = (Vector2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + float du = urange / (float)(uverts - 1); + float dv = vrange / (float)(vverts - 1); + + float u = 0.0; + for(int i=0; iclear(); + Vector3 *varr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vector3 *narr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vector3 *tarr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vector2 *uvarr = (Vector2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + float du = urange / (float)(uverts - 1); + float dv = vrange / (float)(vverts - 1); + + float u = 0.0; + for(int i=0; iclear(); + Vector3 *varr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vector3 *narr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vector3 *tarr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vector2 *uvarr = (Vector2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + float du = urange / (float)(uverts - 1); + float dv = vrange / (float)(vverts - 1); + + float u = 0.0; + for(int i=0; iclear(); + Vector3 *varr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vector3 *narr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vector3 *tarr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vector2 *uvarr = (Vector2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + float du = urange / (float)(uverts - 1); + float dv = vrange / (float)(vverts - 1); + + float u = 0.0; + for(int i=0; iclear(); + + int uverts = usub + 1; + int vverts = vsub + 1; + int num_verts = uverts * vverts; + + int num_quads = usub * vsub; + int num_tri = num_quads * 2; + + Vector3 *varr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vector3 *narr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vector3 *tarr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vector2 *uvarr = (Vector2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + float du = 1.0 / (float)usub; + float dv = 1.0 / (float)vsub; + + float u = 0.0; + for(int i=0; iclear(); + + for(int i=0; i<6; i++) { + Matrix4x4 xform, dir_xform; + Mesh m; + + gen_plane(&m, 1, 1, usub, vsub); + xform.rotate(Vector3(face_angles[i][1], face_angles[i][0], 0)); + dir_xform = xform; + xform.translate(Vector3(0, 0, 0.5)); + m.apply_xform(xform, dir_xform); + + mesh->append(m); + } + + Matrix4x4 scale; + scale.set_scaling(Vector3(xsz, ysz, zsz)); + mesh->apply_xform(scale, Matrix4x4::identity); +} + +/* +void gen_box(Mesh *mesh, float xsz, float ysz, float zsz) +{ + mesh->clear(); + + const int num_faces = 6; + int num_verts = num_faces * 4; + int num_tri = num_faces * 2; + + float x = xsz / 2.0; + float y = ysz / 2.0; + float z = zsz / 2.0; + + Vector3 *varr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vector3 *narr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vector3 *tarr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vector2 *uvarr = (Vector2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + static const Vector2 uv[] = { Vector2(0, 0), Vector2(1, 0), Vector2(1, 1), Vector2(0, 1) }; + + // front + for(int i=0; i<4; i++) { + *narr++ = Vector3(0, 0, 1); + *tarr++ = Vector3(1, 0, 0); + *uvarr++ = uv[i]; + } + *varr++ = Vector3(-x, -y, z); + *varr++ = Vector3(x, -y, z); + *varr++ = Vector3(x, y, z); + *varr++ = Vector3(-x, y, z); + // right + for(int i=0; i<4; i++) { + *narr++ = Vector3(1, 0, 0); + *tarr++ = Vector3(0, 0, -1); + *uvarr++ = uv[i]; + } + *varr++ = Vector3(x, -y, z); + *varr++ = Vector3(x, -y, -z); + *varr++ = Vector3(x, y, -z); + *varr++ = Vector3(x, y, z); + // back + for(int i=0; i<4; i++) { + *narr++ = Vector3(0, 0, -1); + *tarr++ = Vector3(-1, 0, 0); + *uvarr++ = uv[i]; + } + *varr++ = Vector3(x, -y, -z); + *varr++ = Vector3(-x, -y, -z); + *varr++ = Vector3(-x, y, -z); + *varr++ = Vector3(x, y, -z); + // left + for(int i=0; i<4; i++) { + *narr++ = Vector3(-1, 0, 0); + *tarr++ = Vector3(0, 0, 1); + *uvarr++ = uv[i]; + } + *varr++ = Vector3(-x, -y, -z); + *varr++ = Vector3(-x, -y, z); + *varr++ = Vector3(-x, y, z); + *varr++ = Vector3(-x, y, -z); + // top + for(int i=0; i<4; i++) { + *narr++ = Vector3(0, 1, 0); + *tarr++ = Vector3(1, 0, 0); + *uvarr++ = uv[i]; + } + *varr++ = Vector3(-x, y, z); + *varr++ = Vector3(x, y, z); + *varr++ = Vector3(x, y, -z); + *varr++ = Vector3(-x, y, -z); + // bottom + for(int i=0; i<4; i++) { + *narr++ = Vector3(0, -1, 0); + *tarr++ = Vector3(1, 0, 0); + *uvarr++ = uv[i]; + } + *varr++ = Vector3(-x, -y, -z); + *varr++ = Vector3(x, -y, -z); + *varr++ = Vector3(x, -y, z); + *varr++ = Vector3(-x, -y, z); + + // index array + static const int faceidx[] = {0, 1, 2, 0, 2, 3}; + for(int i=0; iclear(); + + int uverts = usub + 1; + int vverts = vsub + 1; + int num_verts = uverts * vverts; + + int num_quads = usub * vsub; + int num_tri = num_quads * 2; + + Vector3 *varr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vector3 *narr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vector3 *tarr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vector2 *uvarr = (Vector2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + float du = 1.0 / (float)(uverts - 1); + float dv = 1.0 / (float)(vverts - 1); + + float u = 0.0; + for(int i=0; i 0.5 ? v - dv * 0.25 : v + dv * 0.25; + nextu = rev_vert(fmod(u + du, 1.0), new_v, rfunc, cls); + tang = nextu - pos; + } + + Vector3 normal; + if(nfunc) { + normal = rev_vert(u, v, nfunc, cls); + } else { + Vector3 nextv = rev_vert(u, v + dv, rfunc, cls); + Vector3 bitan = nextv - pos; + if(bitan.length_sq() < 1e-6) { + nextv = rev_vert(u, v - dv, rfunc, cls); + bitan = pos - nextv; + } + + normal = cross_product(tang, bitan); + } + + *varr++ = pos; + *narr++ = normal.normalized(); + *tarr++ = tang.normalized(); + *uvarr++ = Vector2(u, v); + + if(i < usub && j < vsub) { + int idx = i * vverts + j; + + *idxarr++ = idx; + *idxarr++ = idx + vverts + 1; + *idxarr++ = idx + 1; + + *idxarr++ = idx; + *idxarr++ = idx + vverts; + *idxarr++ = idx + vverts + 1; + } + + v += dv; + } + u += du; + } +} + + +static inline Vector3 sweep_vert(float u, float v, float height, Vector2 (*sf)(float, float, void*), void *cls) +{ + Vector2 pos = sf(u, v, cls); + + float x = pos.x; + float y = v * height; + float z = pos.y; + + return Vector3(x, y, z); +} + +// ---- sweep shape along a path ---- +void gen_sweep(Mesh *mesh, float height, int usub, int vsub, Vector2 (*sfunc)(float, float, void*), void *cls) +{ + if(!sfunc) return; + if(usub < 3) usub = 3; + if(vsub < 1) vsub = 1; + + mesh->clear(); + + int uverts = usub + 1; + int vverts = vsub + 1; + int num_verts = uverts * vverts; + + int num_quads = usub * vsub; + int num_tri = num_quads * 2; + + Vector3 *varr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vector3 *narr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vector3 *tarr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vector2 *uvarr = (Vector2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + float du = 1.0 / (float)(uverts - 1); + float dv = 1.0 / (float)(vverts - 1); + + float u = 0.0; + for(int i=0; i 0.5 ? v - dv * 0.25 : v + dv * 0.25; + nextu = sweep_vert(fmod(u + du, 1.0), new_v, height, sfunc, cls); + tang = nextu - pos; + } + + Vector3 normal; + Vector3 nextv = sweep_vert(u, v + dv, height, sfunc, cls); + Vector3 bitan = nextv - pos; + if(bitan.length_sq() < 1e-6) { + nextv = sweep_vert(u, v - dv, height, sfunc, cls); + bitan = pos - nextv; + } + + normal = cross_product(tang, bitan); + + *varr++ = pos; + *narr++ = normal.normalized(); + *tarr++ = tang.normalized(); + *uvarr++ = Vector2(u, v); + + if(i < usub && j < vsub) { + int idx = i * vverts + j; + + *idxarr++ = idx; + *idxarr++ = idx + vverts + 1; + *idxarr++ = idx + 1; + + *idxarr++ = idx; + *idxarr++ = idx + vverts; + *idxarr++ = idx + vverts + 1; + } + + v += dv; + } + u += du; + } +} diff -r 000000000000 -r 87dfe0e10235 src/meshgen.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/meshgen.h Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,22 @@ +#ifndef MESHGEN_H_ +#define MESHGEN_H_ + +#include "vmath/vmath.h" + +class Mesh; + +void gen_sphere(Mesh *mesh, float rad, int usub, int vsub, float urange = 1.0, float vrange = 1.0); +void gen_torus(Mesh *mesh, float mainrad, float ringrad, int usub, int vsub, float urange = 1.0, float vrange = 1.0); +void gen_cylinder(Mesh *mesh, float rad, float height, int usub, int vsub, int capsub = 0, float urange = 1.0, float vrange = 1.0); +void gen_cone(Mesh *mesh, float rad, float height, int usub, int vsub, int capsub = 0, float urange = 1.0, float vrange = 1.0); +void gen_plane(Mesh *mesh, float width, float height, int usub = 1, int vsub = 1); +void gen_heightmap(Mesh *mesh, float width, float height, int usub, int vsub, float (*hf)(float, float, void*), void *hfdata = 0); +void gen_box(Mesh *mesh, float xsz, float ysz, float zsz, int usub = 1, int vsub = 1); + +void gen_revol(Mesh *mesh, int usub, int vsub, Vector2 (*rfunc)(float, float, void*), void *cls = 0); +void gen_revol(Mesh *mesh, int usub, int vsub, Vector2 (*rfunc)(float, float, void*), Vector2 (*nfunc)(float, float, void*), void *cls); + +/* callback args: (float u, float v, void *cls) -> Vector2 XZ offset u,v in [0, 1] */ +void gen_sweep(Mesh *mesh, float height, int usub, int vsub, Vector2 (*sfunc)(float, float, void*), void *cls = 0); + +#endif // MESHGEN_H_ diff -r 000000000000 -r 87dfe0e10235 src/object.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/object.cc Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,232 @@ +#include "object.h" +#include "opengl.h" +#include "shadow.h" +#include "shader.h" + +Material::Material() + : diffuse(1, 1, 1), specular(0, 0, 0) +{ + shininess = 60.0; + alpha = 1.0; +} + +RenderOps::RenderOps() +{ + zwrite = true; + cast_shadows = true; + transparent = false; +} + +void RenderOps::setup() const +{ + if(!zwrite) { + glDepthMask(0); + } + if(transparent) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } else { + glDisable(GL_BLEND); + } +} + +Object::Object() +{ + mesh = 0; + tex = 0; + sdr = 0; +} + +Object::~Object() +{ + delete mesh; +} + +Matrix4x4 &Object::xform() +{ + return matrix; +} + +const Matrix4x4 &Object::xform() const +{ + return matrix; +} + +Matrix4x4 &Object::tex_xform() +{ + return tex_matrix; +} + +const Matrix4x4 &Object::tex_xform() const +{ + return tex_matrix; +} + +void Object::set_mesh(Mesh *m) +{ + this->mesh = m; +} + +Mesh *Object::get_mesh() const +{ + return mesh; +} + +void Object::set_texture(unsigned int tex) +{ + this->tex = tex; +} + +void Object::set_shader(unsigned int sdr) +{ + if(GLEW_ARB_vertex_shader && GLEW_ARB_fragment_shader) { + this->sdr = sdr; + } +} + +unsigned int Object::get_shader() const +{ + return sdr; +} + +void Object::draw() const +{ + if(!mesh) return; + + if(shadow_pass && !rop.cast_shadows) { + return; + } + + glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT); + rop.setup(); + + if(glcaps.shaders) { + if(sdr) { + if(!shadow_pass) { + ::set_shader(sdr); + } + } else { + ::set_shader(0); + } + } + + if(tex) { + glBindTexture(GL_TEXTURE_2D, tex); + glEnable(GL_TEXTURE_2D); + + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glLoadTransposeMatrixf(tex_matrix[0]); + } else { + glDisable(GL_TEXTURE_2D); + } + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glMultTransposeMatrixf(matrix[0]); + + float dcol[] = {mtl.diffuse.x, mtl.diffuse.y, mtl.diffuse.z, mtl.alpha}; + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, dcol); + float scol[] = {mtl.specular.x, mtl.specular.y, mtl.specular.z, 1.0f}; + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, scol); + glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, mtl.shininess); + + mesh->draw(); + + if(tex) { + glDisable(GL_TEXTURE_2D); + + glMatrixMode(GL_TEXTURE); + glPopMatrix(); + } + + if(sdr) { + ::set_shader(0); + } + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + glPopAttrib(); +} + +void Object::draw_wire(const Vector4 &col) const +{ + if(shadow_pass) return; + + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_LIGHTING); + glUseProgram(0); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glMultTransposeMatrixf(matrix[0]); + + glColor4f(col.x, col.y, col.z, col.w); + mesh->draw_wire(); + + glPopMatrix(); + glPopAttrib(); +} + +void Object::draw_vertices(const Vector4 &col) const +{ + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_LIGHTING); + glUseProgram(0); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glMultTransposeMatrixf(matrix[0]); + + glColor4f(col.x, col.y, col.z, col.w); + mesh->draw_vertices(); + + glPopMatrix(); + glPopAttrib(); +} + +void Object::draw_normals(float len, const Vector4 &col) const +{ + int cur_sdr; + glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr); + glUseProgram(0); + + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_LIGHTING); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glMultTransposeMatrixf(matrix[0]); + + glColor4f(col.x, col.y, col.z, col.w); + mesh->set_vis_vecsize(len); + mesh->draw_normals(); + + glPopMatrix(); + glPopAttrib(); + + glUseProgram(cur_sdr); +} + +void Object::draw_tangents(float len, const Vector4 &col) const +{ + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_LIGHTING); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glMultTransposeMatrixf(matrix[0]); + + glColor4f(col.x, col.y, col.z, col.w); + mesh->set_vis_vecsize(len); + mesh->draw_tangents(); + + glPopMatrix(); + glPopAttrib(); +} + +bool Object::intersect(const Ray &ray, HitPoint *hit) const +{ + return false; // TODO +} diff -r 000000000000 -r 87dfe0e10235 src/object.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/object.h Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,63 @@ +#ifndef OBJECT_H_ +#define OBJECT_H_ + +#include "mesh.h" +#include "geom.h" +#include "vmath/vmath.h" + +struct Material { + Vector3 diffuse; + Vector3 specular; + float shininess; + float alpha; + + Material(); +}; + +struct RenderOps { + bool zwrite; + bool cast_shadows; + bool transparent; + + RenderOps(); + void setup() const; +}; + +class Object { +private: + Mesh *mesh; + Matrix4x4 matrix; + unsigned int tex; + Matrix4x4 tex_matrix; + unsigned int sdr; + +public: + Material mtl; + RenderOps rop; + + Object(); + ~Object(); + + Matrix4x4 &xform(); + const Matrix4x4 &xform() const; + + Matrix4x4 &tex_xform(); + const Matrix4x4 &tex_xform() const; + + void set_mesh(Mesh *m); + Mesh *get_mesh() const; + + void set_texture(unsigned int tex); + void set_shader(unsigned int sdr); + unsigned int get_shader() const; + + void draw() const; + void draw_wire(const Vector4 &col = Vector4(1, 1, 1, 1)) const; + void draw_vertices(const Vector4 &col = Vector4(1, 0.3, 0.2, 1)) const; + void draw_normals(float len = 1.0, const Vector4 &col = Vector4(0.1, 0.2, 1.0, 1)) const; + void draw_tangents(float len = 1.0, const Vector4 &col = Vector4(0.1, 1.0, 0.2, 1)) const; + + bool intersect(const Ray &ray, HitPoint *hit) const; +}; + +#endif // OBJECT_H_ diff -r 000000000000 -r 87dfe0e10235 src/opengl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/opengl.c Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,16 @@ +#include "opengl.h" + +struct GLCaps glcaps; + +int init_opengl() +{ + glewInit(); + + glcaps.shaders = GLEW_ARB_vertex_shader && GLEW_ARB_fragment_shader; + glcaps.fsaa = GLEW_ARB_multisample; + glcaps.sep_spec = GLEW_EXT_separate_specular_color; + glcaps.fbo = GLEW_ARB_framebuffer_object; + glcaps.shadow = GLEW_ARB_shadow | GLEW_SGIX_shadow; + + return 0; +} diff -r 000000000000 -r 87dfe0e10235 src/opengl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/opengl.h Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,25 @@ +#ifndef OPENGL_H_ +#define OPENGL_H_ + +#include + +struct GLCaps { + int shaders; + int fsaa; + int sep_spec; + int fbo; + int shadow; +}; +extern struct GLCaps glcaps; + +#ifdef __cplusplus +extern "C" { +#endif + +int init_opengl(); + +#ifdef __cplusplus +} +#endif + +#endif /* OPENGL_H_ */ diff -r 000000000000 -r 87dfe0e10235 src/opt.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/opt.cc Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,31 @@ +#include +#include "opt.h" + +Options opt; + +bool init_options(int argc, char **argv) +{ + opt.xres = 1280; + opt.yres = 800; + opt.fullscreen = false; + opt.shadows = true; + opt.reflections = true; + + for(int i=1; i +#include "pnoise.h" +#include "vmath/vmath.h" + +#define B 0x100 +#define BM 0xff +#define N 0x1000 +#define NP 12 +#define NM 0xfff + +#define s_curve(t) ((t) * (t) * (3.0f - 2.0f * (t))) +#define setup(elem, b0, b1, r0, r1) \ + do { \ + float t = elem + N; \ + b0 = ((int)t) & BM; \ + b1 = (b0 + 1) & BM; \ + r0 = t - (int)t; \ + r1 = r0 - 1.0f; \ + } while(0) + +#define setup_p(elem, b0, b1, r0, r1, p) \ + do { \ + float t = elem + N; \ + b0 = (((int)t) & BM) % p; \ + b1 = ((b0 + 1) & BM) % p; \ + r0 = t - (int)t; \ + r1 = r0 - 1.0f; \ + } while(0) + +static int perm[B + B + 2]; +static vec2_t grad2[B + B + 2]; +static bool tables_valid; + +static void init_noise() +{ + for(int i=0; i +#include "revol.h" + +Vector2 bezier_revol(float u, float v, void *cls) +{ + BezCurve *curve = (BezCurve*)cls; + int nseg = (curve->numcp - 1) / 2; + + if(v >= 1.0) v = 1.0 - 1e-6; + int cidx = std::min((int)(v * nseg), nseg - 1); + float t = fmod(v * (float)nseg, 1.0); + + const vec2_t *cp = curve->cp + cidx * 2; + + float resx = bezier(cp[0].x, cp[1].x, cp[1].x, cp[2].x, t); + float resy = bezier(cp[0].y, cp[1].y, cp[1].y, cp[2].y, t); + return Vector2(resx * curve->scale, resy * curve->scale); +} + +Vector2 bezier_revol_normal(float u, float v, void *cls) +{ + BezCurve *curve = (BezCurve*)cls; + int nseg = (curve->numcp - 1) / 2; + + if(v >= 1.0) v = 1.0 - 1e-6; + int cidx = std::min((int)(v * nseg), nseg - 1); + float t = fmod(v * (float)nseg, 1.0); + + const vec2_t *cp = curve->cp + cidx * 2; + Vector2 cp0 = cp[0]; + Vector2 cp1 = cp[1]; + Vector2 cp2 = cp[2]; + + Vector2 pprev, pnext; + for(int i=0; i<2; i++) { + pprev[i] = bezier(cp0[i], cp1[i], cp1[i], cp2[i], t - 0.05); + pnext[i] = bezier(cp0[i], cp1[i], cp1[i], cp2[i], t + 0.05); + } + + float tx = pnext.x - pprev.x; + float ty = pnext.y - pprev.y; + + return Vector2(-ty, tx); +} diff -r 000000000000 -r 87dfe0e10235 src/revol.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/revol.h Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,15 @@ +#ifndef REVOL_H_ +#define REVOL_H_ + +#include "vmath/vmath.h" + +struct BezCurve { + int numcp; + vec2_t *cp; + float scale; +}; + +Vector2 bezier_revol(float u, float v, void *cls); +Vector2 bezier_revol_normal(float u, float v, void *cls); + +#endif // REVOL_H_ diff -r 000000000000 -r 87dfe0e10235 src/room.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/room.cc Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,73 @@ +#include +#include "opengl.h" +#include "room.h" +#include "game.h" +#include "object.h" +#include "scene.h" +#include "meshgen.h" +#include "revol.h" + +static Scene scn; + +static const vec2_t pillar_cp[] = { + {0.8, 10}, + {1.2, 5.5}, + {1, 0} +}; +static const BezCurve pillar_curve = { + sizeof pillar_cp / sizeof *pillar_cp, + (vec2_t*)pillar_cp, 1.0 +}; + +bool init_room() +{ + Matrix4x4 xform; + + // generate room + Mesh *mroom = new Mesh; + gen_box(mroom, ROOM_WIDTH, ROOM_HEIGHT, ROOM_LENGTH); + xform.set_translation(Vector3(0, ROOM_HEIGHT / 2.0, 0)); + mroom->apply_xform(xform); + mroom->flip(); + + Object *oroom = new Object; + oroom->set_mesh(mroom); + oroom->mtl.diffuse = Vector3(0.5, 0.5, 0.5); + oroom->rop.cast_shadows = false; + scn.add_object(oroom); + + for(int i=0; i<8; i++) { + float x = (i < 4 ? -1.0 : 1.0) * ROOM_WIDTH * 0.3; + float z = (float)(i % 4) * 12.5 - 12.5; + + Mesh *mpillar = new Mesh; + gen_revol(mpillar, 16, 3, bezier_revol, bezier_revol_normal, (void*)&pillar_curve); + + Mesh mtorus; + gen_torus(&mtorus, 1.0, 0.25, 16, 8); + Matrix4x4 xform; + xform.set_translation(Vector3(0, 0.1, 0)); + mtorus.apply_xform(xform, Matrix4x4::identity); + mpillar->append(mtorus); + + mpillar->texcoord_gen_cylinder(); + + Object *opillar = new Object; + opillar->set_mesh(mpillar); + opillar->mtl.diffuse = Vector3(0.6, 0.6, 0.6); + opillar->xform().set_translation(Vector3(x, 0.0, z)); + + scn.add_object(opillar); + } + return true; +} + +void cleanup_room() +{ + scn.clear(); +} + +void draw_room() +{ + scn.draw(); +} diff -r 000000000000 -r 87dfe0e10235 src/room.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/room.h Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,14 @@ +#ifndef ROOM_H_ +#define ROOM_H_ + + +#define ROOM_WIDTH 30.0f +#define ROOM_HEIGHT 10.0f +#define ROOM_LENGTH 50.0f + + +bool init_room(); +void cleanup_room(); +void draw_room(); + +#endif // ROOM_H_ diff -r 000000000000 -r 87dfe0e10235 src/scene.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/scene.cc Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,79 @@ +#include "scene.h" +#include "opengl.h" +#include "opt.h" +#include "game.h" +#include "scnload.h" + +static int max_lights = -1; + +Scene::~Scene() +{ + clear(); +} + +void Scene::clear() +{ + for(size_t i=0; iget_mesh()->dump(fp); + } +} + +void Scene::draw(unsigned int flags) const +{ + if(max_lights == -1) { + glGetIntegerv(GL_MAX_LIGHTS, &max_lights); + printf("max lights: %d\n", max_lights); + } + + for(size_t i=0; isetup(i); + } + + for(size_t i=0; irop.transparent ? DRAW_TRANSPARENT : DRAW_SOLID; + if(mask & flags) { + if(dbg_wireframe) { + objects[i]->draw_wire(); + } else { + objects[i]->draw(); + } + } + } +} diff -r 000000000000 -r 87dfe0e10235 src/scene.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/scene.h Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,33 @@ +#ifndef SCENE_H_ +#define SCENE_H_ + +#include +#include +#include "object.h" +#include "light.h" + +enum { + DRAW_SOLID = 1, + DRAW_TRANSPARENT = 2, + DRAW_ALL = 0x7fffffff +}; + +class Scene { +public: + std::vector objects; + std::vector lights; + + ~Scene(); + + void clear(); + + void add_object(Object *obj); + void add_lights(Light *lt); + + bool load(const char *fname); + void dump(FILE *fp); + + void draw(unsigned int flags = DRAW_ALL) const; +}; + +#endif // SCENE_H_ diff -r 000000000000 -r 87dfe0e10235 src/scnload.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/scnload.h Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,9 @@ +#ifndef SCNLOAD_H_ +#define SCNLOAD_H_ + +class Scene; + +bool load_obj(Scene *scn, const char *fname); +bool load_obj(Scene *scn, FILE *fp); + +#endif /* SCNLOAD_H_ */ diff -r 000000000000 -r 87dfe0e10235 src/scnload_obj.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/scnload_obj.cc Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,263 @@ +#include +#include +#include +#include +#include +#include +#include "mesh.h" +#include "scene.h" +#include "scnload.h" + +enum { + CMD_VERTEX, + CMD_NORMAL, + CMD_TEXCOORD, + CMD_FACE, + CMD_OBJECT, + CMD_GROUP, + CMD_MTLLIB +}; + +static struct { + const char *s; + int cmd; +} commands[] = { + {"v", CMD_VERTEX}, + {"vn", CMD_NORMAL}, + {"vt", CMD_TEXCOORD}, + {"f", CMD_FACE}, + {"o", CMD_OBJECT}, + {"g", CMD_GROUP}, + {"mtllib", CMD_MTLLIB}, + {0, -1} +}; + +struct Face { + int vnum; + int vidx[4]; + int nidx[4]; + int tidx[4]; +}; + +struct ObjMesh { + std::vector varr; + std::vector narr; + std::vector tarr; + std::vector faces; +}; + +struct ParserState { + FILE *fp; + Scene *scn; + const char *fname; + + unsigned int voffs, noffs, toffs; + ObjMesh omesh; +}; + +static bool parse_line(ParserState *ps, char *line); +static void flush_mesh(ParserState *ps); +static char *clean_input(char *s); + +bool load_obj(Scene *scn, const char *fname) +{ + FILE *fp; + char buf[256]; + bool result = false; + ParserState ps; + + if(!(fp = fopen(fname, "r"))) { + fprintf(stderr, "failed to open obj file: %s: %s\n", fname, strerror(errno)); + return false; + } + ps.fp = fp; + ps.scn = scn; + ps.fname = fname; + ps.voffs = ps.noffs = ps.toffs = 0; + + int nline = 0; + while(fgets(buf, sizeof buf, fp)) { + ++nline; + char *line = clean_input(buf); + if(!*line) continue; + + if(!parse_line(&ps, line)) { + fprintf(stderr, "[obj] %s:%d: failed to parse\n", fname, nline); + goto done; + } + } + flush_mesh(&ps); + result = true; + +done: + fclose(fp); + return result; +} + +static int parse_cmd(char *line, char **endp) +{ + char *end = line; + while(*end && !isspace(*end)) end++; + *end = 0; + *endp = end; + + for(int i=0; commands[i].s; i++) { + if(strcmp(commands[i].s, line) == 0) { + return commands[i].cmd; + } + } + return -1; +} + +static bool parse_vec3(Vector3 *vp, char *line) +{ + return sscanf(line, "%f %f %f", &vp->x, &vp->y, &vp->z) == 3; +} + +static bool parse_vec2(Vector2 *vp, char *line) +{ + return sscanf(line, "%f %f", &vp->x, &vp->y) == 2; +} + +#define INVAL_IDX INT_MIN + +static bool parse_face(Face *fp, char *line) +{ + fp->vnum = 0; + + int res; + char *tok, *ptr = line; + for(int i=0; i<4; i++) { + while(*ptr && isspace(*ptr)) ptr++; + if(!*ptr) break; + tok = ptr; + + fp->vnum++; + + while(*ptr && !isspace(*ptr)) ptr++; + if(*ptr) *ptr++ = 0; + + // parse the index descriptor vidx[/[tidx]/[nidx]] + if(sscanf(tok, "%d//%d", &fp->vidx[i], &fp->nidx[i]) == 2) { + fp->tidx[i] = INVAL_IDX; + continue; + } + res = sscanf(tok, "%d/%d/%d", &fp->vidx[i], &fp->tidx[i], &fp->nidx[i]); + if(res > 2) { + if(res != 3) { + fp->nidx[i] = INVAL_IDX; + } + continue; + } + char *endp; + fp->vidx[i] = strtol(tok, &endp, 10); + if(endp == tok) { + return false; + } + fp->nidx[i] = fp->tidx[i] = INVAL_IDX; + } + return fp->vnum >= 3 ? true : false; +} + +static bool parse_line(ParserState *ps, char *line) +{ + char *endp; + int cmd = parse_cmd(line, &endp); + if(cmd == -1) return true; // ignore unknown commands + line = endp + 1; + while(*line && isspace(*line)) line++; + + Vector3 v; + Vector2 v2; + Face f; + + switch(cmd) { + case CMD_VERTEX: + if(!parse_vec3(&v, line)) return false; + ps->omesh.varr.push_back(v); + break; + + case CMD_NORMAL: + if(!parse_vec3(&v, line)) return false; + ps->omesh.narr.push_back(v); + break; + + case CMD_TEXCOORD: + if(!parse_vec2(&v2, line)) return false; + ps->omesh.tarr.push_back(v2); + break; + + case CMD_FACE: + if(!parse_face(&f, line)) return false; + ps->omesh.faces.push_back(f); + break; + + case CMD_OBJECT: + case CMD_GROUP: + flush_mesh(ps); + break; + + default: + break; + } + return true; +} + +static void flush_mesh(ParserState *ps) +{ + if(!ps->omesh.varr.empty() && !ps->omesh.faces.empty()) { + int num_verts = (int)ps->omesh.varr.size(); + int num_faces = (int)ps->omesh.faces.size(); + + Mesh *m = new Mesh; + m->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, (float*)&ps->omesh.varr[0]); + + if(!ps->omesh.narr.empty()) { + m->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, (float*)&ps->omesh.narr[0]); + } + if(!ps->omesh.tarr.empty()) { + m->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, (float*)&ps->omesh.tarr[0]); + } + + unsigned int *idxarr = m->set_index_data(num_faces * 3); + for(int i=0; iomesh.faces[i].vidx[j] - ps->voffs - 1; // TODO normalize indices + *idxarr++ = idx; + } + } + + Object *o = new Object; + o->set_mesh(m); + + // TODO material & textures + + ps->scn->add_object(o); + + printf("added object with %d vertices and %d faces\n", num_verts, num_faces); + } + + ps->voffs += ps->omesh.varr.size(); + ps->noffs += ps->omesh.narr.size(); + ps->toffs += ps->omesh.tarr.size(); + + ps->omesh.varr.clear(); + ps->omesh.narr.clear(); + ps->omesh.tarr.clear(); + ps->omesh.faces.clear(); +} + +static char *clean_input(char *s) +{ + while(*s && isspace(*s)) s++; + if(!*s) return s; + + char *end = strchr(s, '#'); + if(end) *end = 0; + + end = s + strlen(s) - 1; + while(end > s && isspace(*end)) end--; + end[1] = 0; + + return s; +} diff -r 000000000000 -r 87dfe0e10235 src/sdr.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sdr.c Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,407 @@ +#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); + +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; + GLenum err; + + sdr = glCreateShader(sdr_type); + assert(glGetError() == GL_NO_ERROR); + glShaderSource(sdr, 1, &src, 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 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, 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, 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); +} + +static const char *sdrtypestr(unsigned int sdrtype) +{ + switch(sdrtype) { + case GL_VERTEX_SHADER: + return "vertex"; + case GL_FRAGMENT_SHADER: + return "pixel"; +#ifdef GL_TESS_CONTROL_SHADER + case GL_TESS_CONTROL_SHADER: + return "tessellation control"; +#endif +#ifdef GL_TESS_EVALUATION_SHADER + case GL_TESS_EVALUATION_SHADER: + return "tessellation evaluation"; +#endif +#ifdef GL_GEOMETRY_SHADER + case GL_GEOMETRY_SHADER: + return "geometry"; +#endif + + default: + break; + } + return ""; +} diff -r 000000000000 -r 87dfe0e10235 src/sdr.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sdr.h Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,52 @@ +#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 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, float *mat); +int set_uniform_matrix4_transposed(unsigned int prog, const char *name, 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); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SDR_H_ */ diff -r 000000000000 -r 87dfe0e10235 src/shader.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/shader.cc Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,28 @@ +#include "shader.h" +#include "sdr.h" + +static unsigned int cur_sdr; +static unsigned int sover; + +void set_shader(unsigned int sdr) +{ + cur_sdr = sdr; + if(!sover) { + bind_program(sdr); + } +} + +unsigned int current_shader() +{ + return sover ? sover : cur_sdr; +} + +void override_shader(unsigned int sdr) +{ + sover = sdr; + if(sover) { + bind_program(sdr); + } else { + bind_program(cur_sdr); + } +} diff -r 000000000000 -r 87dfe0e10235 src/shader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/shader.h Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,8 @@ +#ifndef SHADER_H_ +#define SHADER_H_ + +void set_shader(unsigned int sdr); +unsigned int current_shader(); +void override_shader(unsigned int sdr); + +#endif /* SHADER_H_ */ diff -r 000000000000 -r 87dfe0e10235 src/shadow.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/shadow.cc Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,141 @@ +#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 Matrix4x4 shadow_mat; + +bool init_shadow(int sz) +{ + if(!glcaps.fbo || !glcaps.shadow) { + return false; + } + + tex_sz = sz; + printf("initializing shadow buffer (%dx%d)\n", tex_sz, tex_sz); + + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + glGenTextures(1, &depth_tex); + glBindTexture(GL_TEXTURE_2D, depth_tex); + float border[] = {1, 1, 1, 1}; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + 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 Vector3 &lpos, const Vector3 <arg, float lfov) +{ + 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, 8.0, 50.0); + lt_viewmat.set_lookat(lpos, ltarg, Vector3(0, 1, 0)); + shadow_mat = lt_projmat * lt_viewmat * viewmat.inverse(); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadTransposeMatrixf(lt_projmat[0]); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadTransposeMatrixf(lt_viewmat[0]); + + glGetIntegerv(GL_VIEWPORT, prev_vp); + glViewport(0, 0, tex_sz, tex_sz); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + glPolygonOffset(2.0, 4.0); + 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(); +} + +Matrix4x4 get_shadow_matrix() +{ + return shadow_mat; + + /* + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + gluPerspective(lfov * 2.0, 1.0, 0.5, 50.0); + gluLookAt(lpos.x, lpos.y, lpos.z, ltarg.x, ltarg.y, ltarg.z, 0, 1, 0); + + float mat[16]; + glGetFloatv(GL_MODELVIEW_MATRIX, mat); + + Matrix4x4 res; + memcpy(res[0], mat, sizeof mat); + res.transpose(); + return res; + */ +} + +unsigned int get_shadow_tex() +{ + return depth_tex; +} diff -r 000000000000 -r 87dfe0e10235 src/shadow.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/shadow.h Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,17 @@ +#ifndef SHADOW_H_ +#define SHADOW_H_ + +#include "vmath/vmath.h" + +extern bool shadow_pass; + +bool init_shadow(int sz); +void destroy_shadow(); + +void begin_shadow_pass(const Vector3 &lpos, const Vector3 <arg, float lfov); +void end_shadow_pass(); + +Matrix4x4 get_shadow_matrix(); +unsigned int get_shadow_tex(); + +#endif // SHADOW_H_ diff -r 000000000000 -r 87dfe0e10235 src/snode.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/snode.cc Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,188 @@ +#include +#include +#include +#include "snode.h" + +SceneNode::SceneNode() + : scale(1, 1, 1) +{ + parent = 0; +} + +SceneNode::SceneNode(Object *obj) + : scale(1, 1, 1) +{ + parent = 0; + add_object(obj); +} + +void SceneNode::add_child(SceneNode *node) +{ + if(node->parent) { + if(node->parent == this) { + return; + } + node->parent->remove_child(node); + } + + children.push_back(node); + node->parent = this; +} + +bool SceneNode::remove_child(SceneNode *node) +{ + for(size_t i=0; iparent == this); + node->parent = 0; + return true; + } + } + return false; +} + +int SceneNode::get_num_children() const +{ + return (int)children.size(); +} + +SceneNode *SceneNode::get_child(int idx) const +{ + return children[idx]; +} + +SceneNode *SceneNode::get_parent() const +{ + return parent; +} + +void SceneNode::add_object(Object *obj) +{ + if(std::find(this->obj.begin(), this->obj.end(), obj) == this->obj.end()) { + this->obj.push_back(obj); + } +} + +int SceneNode::get_num_objects() const +{ + return (int)obj.size(); +} + +Object *SceneNode::get_object(int idx) const +{ + return obj[idx]; +} + +void SceneNode::set_position(const Vector3 &pos) +{ + this->pos = pos; +} + +void SceneNode::set_rotation(const Quaternion &rot) +{ + this->rot = rot; +} + +void SceneNode::set_scaling(const Vector3 &scale) +{ + this->scale = scale; +} + + +const Vector3 &SceneNode::get_node_position() const +{ + return pos; +} + +const Quaternion &SceneNode::get_node_rotation() const +{ + return rot; +} + +const Vector3 &SceneNode::get_node_scaling() const +{ + return scale; +} + + +Vector3 SceneNode::get_position() const +{ + return Vector3(0, 0, 0).transformed(xform); +} + +Quaternion SceneNode::get_rotation() const +{ + return rot; // TODO +} + +Vector3 SceneNode::get_scaling() const +{ + return scale; // TODO +} + +const Matrix4x4 &SceneNode::get_matrix() const +{ + return xform; +} + +const Matrix4x4 &SceneNode::get_inv_matrix() const +{ + return inv_xform; +} + + +void SceneNode::update_node(long msec) +{ + xform.reset_identity(); + xform.translate(pos); + xform.rotate(rot); + xform.scale(scale); + + if(parent) { + xform = parent->xform * xform; + } + inv_xform = xform.inverse(); +} + +void SceneNode::update(long msec) +{ + update_node(msec); + + for(size_t i=0; iupdate(msec); + } +} + + +bool SceneNode::intersect(const Ray &ray, HitPoint *hit) const +{ + Ray local_ray = ray.transformed(inv_xform); + + HitPoint nearest; + nearest.dist = FLT_MAX; + for(size_t i=0; iintersect(local_ray, hit)) { + if(!hit) return true; + if(hit->dist < nearest.dist) { + nearest = *hit; + nearest.node = this; + } + } + } + + for(size_t i=0; iintersect(ray, hit)) { + if(!hit) return true; + if(hit->dist < nearest.dist) { + nearest = *hit; + } + } + } + + if(nearest.dist < FLT_MAX) { + *hit = nearest; + hit->ray = ray; + return true; + } + return false; +} diff -r 000000000000 -r 87dfe0e10235 src/snode.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/snode.h Mon Aug 31 07:38:37 2015 +0300 @@ -0,0 +1,60 @@ +#ifndef SNODE_H_ +#define SNODE_H_ + +#include +#include "object.h" +#include "vmath/vmath.h" +#include "geom.h" + +class SceneNode { +private: + Vector3 pos; + Quaternion rot; + Vector3 scale; + + std::vector obj; + + SceneNode *parent; + std::vector children; + + Matrix4x4 xform; + Matrix4x4 inv_xform; + +public: + SceneNode(); + explicit SceneNode(Object *obj); + + void add_child(SceneNode *node); + bool remove_child(SceneNode *node); + + int get_num_children() const; + SceneNode *get_child(int idx) const; + + SceneNode *get_parent() const; + + void add_object(Object *obj); + int get_num_objects() const; + Object *get_object(int idx) const; + + void set_position(const Vector3 &pos); + void set_rotation(const Quaternion &rot); + void set_scaling(const Vector3 &scale); + + const Vector3 &get_node_position() const; + const Quaternion &get_node_rotation() const; + const Vector3 &get_node_scaling() const; + + Vector3 get_position() const; + Quaternion get_rotation() const; + Vector3 get_scaling() const; + + const Matrix4x4 &get_matrix() const; + const Matrix4x4 &get_inv_matrix() const; + + void update_node(long msec = 0); + void update(long msec = 0); + + bool intersect(const Ray &ray, HitPoint *hit) const; +}; + +#endif // SNODE_H_