tavli

changeset 4:b41ceead1708

procedural playing field texture mask
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 25 Jun 2015 05:58:35 +0300
parents 94aff2ff1934
children e48b40a3c82a
files src/board.cc src/board.h src/image.cc src/image.h src/object.cc src/object.h
diffstat 6 files changed, 419 insertions(+), 40 deletions(-) [+]
line diff
     1.1 --- a/src/board.cc	Mon Jun 22 21:46:57 2015 +0300
     1.2 +++ b/src/board.cc	Thu Jun 25 05:58:35 2015 +0300
     1.3 @@ -16,6 +16,9 @@
     1.4  
     1.5  bool Board::init()
     1.6  {
     1.7 +	if(!generate_textures()) {
     1.8 +		return false;
     1.9 +	}
    1.10  	if(!generate()) {
    1.11  		return false;
    1.12  	}
    1.13 @@ -57,65 +60,75 @@
    1.14  
    1.15  bool Board::generate()
    1.16  {
    1.17 +	Mesh tmp;
    1.18  	Matrix4x4 xform;
    1.19  
    1.20  	obj.clear();
    1.21  
    1.22 -	// generate bottom
    1.23 -	Mesh *bottom = new Mesh;
    1.24 -	gen_box(bottom, HSIZE, BOT_THICKNESS, HSIZE * 2.0);
    1.25 -	xform.set_translation(Vector3(0, -BOT_THICKNESS / 2.0, 0));
    1.26 -	bottom->apply_xform(xform);
    1.27 +	for(int i=0; i<2; i++) {
    1.28 +		int sign = i * 2 - 1;
    1.29  
    1.30 -	Object *obottom = new Object;
    1.31 -	obottom->set_mesh(bottom);
    1.32 -	obj.push_back(obottom);
    1.33 +		// generate bottom
    1.34 +		Mesh *bottom = new Mesh;
    1.35 +		gen_box(bottom, HSIZE, BOT_THICKNESS, HSIZE * 2.0);
    1.36 +		xform.set_translation(Vector3(0, -BOT_THICKNESS / 2.0, 0));
    1.37 +		bottom->apply_xform(xform);
    1.38  
    1.39 +		Object *obottom = new Object;
    1.40 +		obottom->set_mesh(bottom);
    1.41 +		obottom->xform().set_translation(Vector3(sign * (HSIZE / 2.0 + WALL_THICKNESS + HINGE_RAD * 0.25), 0, 0));
    1.42 +		obottom->set_texture(img_field.texture());
    1.43 +		obj.push_back(obottom);
    1.44  
    1.45 -	// generate the 4 sides
    1.46 -	Mesh *sides = new Mesh;
    1.47 -	gen_box(sides, WALL_THICKNESS, WALL_HEIGHT, VSIZE + WALL_THICKNESS * 2);
    1.48 -	xform.set_translation(Vector3(-(HSIZE + WALL_THICKNESS) / 2.0,
    1.49 -				WALL_HEIGHT / 2.0 - BOT_THICKNESS, 0));
    1.50 -	sides->apply_xform(xform);
    1.51  
    1.52 -	Mesh tmp;
    1.53 -	gen_box(&tmp, WALL_THICKNESS, WALL_HEIGHT, VSIZE + WALL_THICKNESS * 2);
    1.54 -	xform.set_translation(Vector3((HSIZE + WALL_THICKNESS) / 2.0,
    1.55 -				WALL_HEIGHT / 2.0 - BOT_THICKNESS, 0));
    1.56 -	tmp.apply_xform(xform);
    1.57 -	sides->append(tmp);
    1.58 -	tmp.clear();
    1.59 +		// generate the 4 sides
    1.60 +		Mesh *sides = new Mesh;
    1.61 +		gen_box(sides, WALL_THICKNESS, WALL_HEIGHT, VSIZE + WALL_THICKNESS * 2);
    1.62 +		xform.set_translation(Vector3(-(HSIZE + WALL_THICKNESS) / 2.0,
    1.63 +					WALL_HEIGHT / 2.0 - BOT_THICKNESS, 0));
    1.64 +		sides->apply_xform(xform);
    1.65  
    1.66 -	gen_box(&tmp, HSIZE, WALL_HEIGHT, WALL_THICKNESS);
    1.67 -	xform.set_translation(Vector3(0, WALL_HEIGHT / 2.0 - BOT_THICKNESS,
    1.68 -				(VSIZE + WALL_THICKNESS) / 2.0));
    1.69 -	tmp.apply_xform(xform);
    1.70 -	sides->append(tmp);
    1.71 -	tmp.clear();
    1.72 +		gen_box(&tmp, WALL_THICKNESS, WALL_HEIGHT, VSIZE + WALL_THICKNESS * 2);
    1.73 +		xform.set_translation(Vector3((HSIZE + WALL_THICKNESS) / 2.0,
    1.74 +					WALL_HEIGHT / 2.0 - BOT_THICKNESS, 0));
    1.75 +		tmp.apply_xform(xform);
    1.76 +		sides->append(tmp);
    1.77 +		tmp.clear();
    1.78  
    1.79 -	gen_box(&tmp, HSIZE, WALL_HEIGHT, WALL_THICKNESS);
    1.80 -	xform.set_translation(Vector3(0, WALL_HEIGHT / 2.0 - BOT_THICKNESS,
    1.81 -				-(VSIZE + WALL_THICKNESS) / 2.0));
    1.82 -	tmp.apply_xform(xform);
    1.83 -	sides->append(tmp);
    1.84 -	tmp.clear();
    1.85 +		gen_box(&tmp, HSIZE, WALL_HEIGHT, WALL_THICKNESS);
    1.86 +		xform.set_translation(Vector3(0, WALL_HEIGHT / 2.0 - BOT_THICKNESS,
    1.87 +					(VSIZE + WALL_THICKNESS) / 2.0));
    1.88 +		tmp.apply_xform(xform);
    1.89 +		sides->append(tmp);
    1.90 +		tmp.clear();
    1.91  
    1.92 -	Object *osides = new Object;
    1.93 -	osides->set_mesh(sides);
    1.94 -	obj.push_back(osides);
    1.95 +		gen_box(&tmp, HSIZE, WALL_HEIGHT, WALL_THICKNESS);
    1.96 +		xform.set_translation(Vector3(0, WALL_HEIGHT / 2.0 - BOT_THICKNESS,
    1.97 +					-(VSIZE + WALL_THICKNESS) / 2.0));
    1.98 +		tmp.apply_xform(xform);
    1.99 +		sides->append(tmp);
   1.100 +		tmp.clear();
   1.101 +
   1.102 +		Object *osides = new Object;
   1.103 +		osides->set_mesh(sides);
   1.104 +		osides->xform() = obottom->xform();
   1.105 +		obj.push_back(osides);
   1.106 +
   1.107 +	}
   1.108  
   1.109  
   1.110  	// generate the hinges
   1.111  	Mesh *hinges = new Mesh;
   1.112  	gen_cylinder(hinges, HINGE_RAD, HINGE_HEIGHT, 10, 1, 1);
   1.113 -	xform.set_rotation(Vector3(M_PI / 2.0, 0, 0));
   1.114 -	xform.translate(Vector3(0, VSIZE / 4.0, 0));
   1.115 +	xform.reset_identity();
   1.116 +	xform.translate(Vector3(0, WALL_HEIGHT - HINGE_RAD * 0.5, VSIZE / 4.0));
   1.117 +	xform.rotate(Vector3(M_PI / 2.0, 0, 0));
   1.118  	hinges->apply_xform(xform);
   1.119  
   1.120  	gen_cylinder(&tmp, HINGE_RAD, HINGE_HEIGHT, 10, 1, 1);
   1.121 -	xform.set_rotation(Vector3(M_PI / 2.0, 0, 0));
   1.122 -	xform.translate(Vector3(0, -VSIZE / 4.0, 0));
   1.123 +	xform.reset_identity();
   1.124 +	xform.translate(Vector3(0, WALL_HEIGHT - HINGE_RAD * 0.5, -VSIZE / 4.0));
   1.125 +	xform.rotate(Vector3(M_PI / 2.0, 0, 0));
   1.126  	tmp.apply_xform(xform);
   1.127  
   1.128  	hinges->append(tmp);
   1.129 @@ -127,3 +140,73 @@
   1.130  
   1.131  	return true;
   1.132  }
   1.133 +
   1.134 +static bool spike(float x, float y)
   1.135 +{
   1.136 +	x = fmod(x * 5.0, 1.0);
   1.137 +	return y < (x < 0.5 ? 2.0 * x : 2.0 - 2.0 * x);
   1.138 +}
   1.139 +
   1.140 +static bool circle(float x, float y, float rad)
   1.141 +{
   1.142 +	x = fmod(x * 5.0, 1.0) - 0.5;
   1.143 +	y = (y - 0.65) * 5.0;
   1.144 +	float len = sqrt(x * x + y * y);
   1.145 +	return len < rad;
   1.146 +}
   1.147 +
   1.148 +static bool diamond(float x, float y)
   1.149 +{
   1.150 +	return y >= (1.0 - (x < 0.5 ? 2.0 * x : 2.0 - 2.0 * x)) * 0.3333333 + 0.88;
   1.151 +}
   1.152 +
   1.153 +static bool center_circle(float x, float y, float rad)
   1.154 +{
   1.155 +	x = x - 0.5;
   1.156 +	y = 1.0 - y;
   1.157 +	return sqrt(x * x + y * y) < rad;
   1.158 +}
   1.159 +
   1.160 +bool Board::generate_textures()
   1.161 +{
   1.162 +	const int xsz = 512;
   1.163 +	const int ysz = 1024;
   1.164 +
   1.165 +	img_field.create(xsz, ysz);
   1.166 +	clear_image(&img_field, 0, 0, 0);
   1.167 +
   1.168 +	unsigned char *pptr = img_field.pixels;
   1.169 +
   1.170 +	for(int i=0; i<ysz; i++) {
   1.171 +		float v = (float)i / (float)ysz;
   1.172 +
   1.173 +		for(int j=0; j<xsz; j++) {
   1.174 +			float u = (float)j / (float)xsz;
   1.175 +
   1.176 +			int r = 0, g = 0, b = 0;
   1.177 +
   1.178 +			float x = u;
   1.179 +			float y = v < 0.5 ? v * 2.0 : 2.0 - v * 2.0;
   1.180 +			bool inside = false;
   1.181 +
   1.182 +			inside |= (spike(x, y + 0.33333) && !spike(x, y + 0.4)) ||
   1.183 +				(spike(x, y + 0.5) && !spike(x, y + 0.68));
   1.184 +			inside |= (circle(x, y, 0.12) && !circle(x, y, 0.1)) || circle(x, y, 0.06);
   1.185 +			inside |= (diamond(x, y) && !diamond(x, y - 0.015)) ||
   1.186 +				(diamond(x, y - 0.023) && !diamond(x, y - 0.028));
   1.187 +			inside |= center_circle(x, y, 0.03);
   1.188 +
   1.189 +			if(inside) {
   1.190 +				r = g = b = 255;
   1.191 +			}
   1.192 +
   1.193 +			pptr[0] = r;
   1.194 +			pptr[1] = g;
   1.195 +			pptr[2] = b;
   1.196 +			pptr += 3;
   1.197 +		}
   1.198 +	}
   1.199 +
   1.200 +	img_field.texture();
   1.201 +	return true;
   1.202 +}
     2.1 --- a/src/board.h	Mon Jun 22 21:46:57 2015 +0300
     2.2 +++ b/src/board.h	Thu Jun 25 05:58:35 2015 +0300
     2.3 @@ -3,6 +3,7 @@
     2.4  
     2.5  #include <vector>
     2.6  #include "object.h"
     2.7 +#include "image.h"
     2.8  
     2.9  #define NUM_SLOTS	24
    2.10  #define MAX_PUCKS	30
    2.11 @@ -15,7 +16,10 @@
    2.12  	std::vector<Object*> obj;
    2.13  	Object *puck_obj;
    2.14  
    2.15 +	Image img_wood, img_field;
    2.16 +
    2.17  	bool generate();
    2.18 +	bool generate_textures();
    2.19  
    2.20  public:
    2.21  	Board();
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/src/image.cc	Thu Jun 25 05:58:35 2015 +0300
     3.3 @@ -0,0 +1,228 @@
     3.4 +#include <string.h>
     3.5 +#include "opengl.h"
     3.6 +#include "image.h"
     3.7 +#include "imago2.h"
     3.8 +
     3.9 +
    3.10 +static unsigned int next_pow2(unsigned int x);
    3.11 +
    3.12 +Image::Image()
    3.13 +{
    3.14 +	width = height = tex_width = tex_height = 0;
    3.15 +	pixels = 0;
    3.16 +	tex = 0;
    3.17 +	tex_valid = false;
    3.18 +}
    3.19 +
    3.20 +Image::~Image()
    3.21 +{
    3.22 +	destroy();
    3.23 +}
    3.24 +
    3.25 +Image::Image(const Image &img)
    3.26 +{
    3.27 +	pixels = 0;
    3.28 +	create(img.width, img.height, img.pixels);
    3.29 +}
    3.30 +
    3.31 +Image &Image::operator =(const Image &img)
    3.32 +{
    3.33 +	if(&img != this) {
    3.34 +		create(img.width, img.height, img.pixels);
    3.35 +	}
    3.36 +	return *this;
    3.37 +}
    3.38 +
    3.39 +bool Image::create(int width, int height, unsigned char *pixels)
    3.40 +{
    3.41 +	destroy();
    3.42 +
    3.43 +	try {
    3.44 +		unsigned char *tmp = new unsigned char[width * height * 3];
    3.45 +		this->pixels = tmp;
    3.46 +		this->width = width;
    3.47 +		this->height = height;
    3.48 +	}
    3.49 +	catch(...) {
    3.50 +		return false;
    3.51 +	}
    3.52 +
    3.53 +	if(pixels) {
    3.54 +		memcpy(this->pixels, pixels, width * height * 3);
    3.55 +	}
    3.56 +	return true;
    3.57 +}
    3.58 +
    3.59 +void Image::destroy()
    3.60 +{
    3.61 +	delete [] pixels;
    3.62 +	pixels = 0;
    3.63 +	width = height = 0;
    3.64 +
    3.65 +	if(tex) {
    3.66 +		glDeleteTextures(1, &tex);
    3.67 +		tex = 0;
    3.68 +		tex_valid = false;
    3.69 +	}
    3.70 +}
    3.71 +
    3.72 +bool Image::load(const char *fname)
    3.73 +{
    3.74 +	int xsz, ysz;
    3.75 +	unsigned char *pix = (unsigned char*)img_load_pixels(fname, &xsz, &ysz, IMG_FMT_RGB24);
    3.76 +	if(!pix) {
    3.77 +		return false;
    3.78 +	}
    3.79 +	return create(xsz, ysz, pix);
    3.80 +}
    3.81 +
    3.82 +unsigned int Image::texture() const
    3.83 +{
    3.84 +	if(!pixels) {
    3.85 +		return 0;
    3.86 +	}
    3.87 +	if(!tex) {
    3.88 +		glGenTextures(1, &tex);
    3.89 +	}
    3.90 +
    3.91 +	if(!tex_valid) {
    3.92 +		tex_width = next_pow2(width);
    3.93 +		tex_height = next_pow2(height);
    3.94 +
    3.95 +		glBindTexture(GL_TEXTURE_2D, tex);
    3.96 +		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    3.97 +		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    3.98 +
    3.99 +		if(GLEW_SGIS_generate_mipmap) {
   3.100 +			glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, 1);
   3.101 +
   3.102 +			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex_width, tex_height, 0, GL_RGB, GL_UNSIGNED_BYTE,
   3.103 +					width == tex_width && height == tex_height ? pixels : 0);
   3.104 +			if(width != tex_width || height != tex_height) {
   3.105 +				glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
   3.106 +			}
   3.107 +		} else {
   3.108 +			gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, tex_width, tex_height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
   3.109 +		}
   3.110 +
   3.111 +		if(GLEW_EXT_texture_filter_anisotropic) {
   3.112 +			static float max_aniso = -1.0;
   3.113 +
   3.114 +			if(max_aniso < 0.0) {
   3.115 +				glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_aniso);
   3.116 +				printf("using anisotropic filtering: x%g\n", max_aniso);
   3.117 +			}
   3.118 +
   3.119 +			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso);
   3.120 +		}
   3.121 +		tex_valid = true;
   3.122 +	}
   3.123 +	return tex;
   3.124 +}
   3.125 +
   3.126 +int Image::texture_width() const
   3.127 +{
   3.128 +	return tex_width;
   3.129 +}
   3.130 +
   3.131 +int Image::texture_height() const
   3.132 +{
   3.133 +	return tex_height;
   3.134 +}
   3.135 +
   3.136 +void Image::invalidate_texture()
   3.137 +{
   3.138 +	tex_valid = false;
   3.139 +}
   3.140 +
   3.141 +
   3.142 +void clear_image(Image *img)
   3.143 +{
   3.144 +	clear_image(img, 0, 0, 0);
   3.145 +}
   3.146 +
   3.147 +void clear_image(Image *img, float r, float g, float b)
   3.148 +{
   3.149 +	if(!img->pixels) {
   3.150 +		return;
   3.151 +	}
   3.152 +
   3.153 +	unsigned char col[3];
   3.154 +	unsigned char *ptr = img->pixels;
   3.155 +	int npix = img->width * img->height;
   3.156 +
   3.157 +	col[0] = (int)(r * 255.0);
   3.158 +	col[1] = (int)(g * 255.0);
   3.159 +	col[2] = (int)(b * 255.0);
   3.160 +
   3.161 +	for(int i=0; i<npix; i++) {
   3.162 +		for(int j=0; j<3; j++) {
   3.163 +			ptr[j] = col[j];
   3.164 +		}
   3.165 +		ptr += 3;
   3.166 +	}
   3.167 +}
   3.168 +
   3.169 +bool combine_image(Image *dest, const Image *aimg, const Image *bimg, ImgCombine op, float t)
   3.170 +{
   3.171 +	int xsz = dest->width;
   3.172 +	int ysz = dest->height;
   3.173 +	int npixels = xsz * ysz;
   3.174 +	int nbytes = npixels * 3;
   3.175 +	int tint = (int)(t * 255);
   3.176 +
   3.177 +	if(aimg->width != xsz || bimg->width != xsz || aimg->height != ysz || bimg->height != ysz) {
   3.178 +		return false;
   3.179 +	}
   3.180 +
   3.181 +	unsigned char *dptr = dest->pixels;
   3.182 +	const unsigned char *aptr = aimg->pixels;
   3.183 +	const unsigned char *bptr = bimg->pixels;
   3.184 +
   3.185 +	switch(op) {
   3.186 +	case IMG_OP_ADD:
   3.187 +		for(int i=0; i<nbytes; i++) {
   3.188 +			unsigned int x = *aptr++ + *bptr++;
   3.189 +			*dptr++ = x > 255 ? 255 : x;
   3.190 +		}
   3.191 +		break;
   3.192 +
   3.193 +	case IMG_OP_SUB:
   3.194 +		for(int i=0; i<nbytes; i++) {
   3.195 +			int x = (int)*aptr++ - (int)*bptr++;
   3.196 +			*dptr++ = x < 0 ? 0 : x;
   3.197 +		}
   3.198 +		break;
   3.199 +
   3.200 +	case IMG_OP_MUL:
   3.201 +		for(int i=0; i<nbytes; i++) {
   3.202 +			unsigned int x = ((unsigned int)*aptr++ * (unsigned int)*bptr++) >> 8;
   3.203 +			*dptr++ = x > 255 ? 255 : x;
   3.204 +		}
   3.205 +		break;
   3.206 +
   3.207 +	case IMG_OP_LERP:
   3.208 +		for(int i=0; i<nbytes; i++) {
   3.209 +			int x = (int)*aptr + ((((int)*bptr - (int)*aptr) * tint) >> 8);
   3.210 +			*dptr++ = x > 255 ? 255 : (x < 0 ? 0 : x);
   3.211 +		}
   3.212 +		break;
   3.213 +
   3.214 +	default:
   3.215 +		break;
   3.216 +	}
   3.217 +
   3.218 +	dest->invalidate_texture();
   3.219 +	return true;
   3.220 +}
   3.221 +
   3.222 +static unsigned int next_pow2(unsigned int x)
   3.223 +{
   3.224 +	x--;
   3.225 +	x = (x >> 1) | x;
   3.226 +	x = (x >> 2) | x;
   3.227 +	x = (x >> 4) | x;
   3.228 +	x = (x >> 8) | x;
   3.229 +	x = (x >> 16) | x;
   3.230 +	return x + 1;
   3.231 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/src/image.h	Thu Jun 25 05:58:35 2015 +0300
     4.3 @@ -0,0 +1,44 @@
     4.4 +#ifndef IMAGE_H_
     4.5 +#define IMAGE_H_
     4.6 +
     4.7 +class Image {
     4.8 +private:
     4.9 +	mutable int tex_width, tex_height;
    4.10 +	mutable unsigned int tex;
    4.11 +	mutable bool tex_valid;
    4.12 +
    4.13 +public:
    4.14 +	int width, height;
    4.15 +	unsigned char *pixels;
    4.16 +
    4.17 +	Image();
    4.18 +	~Image();
    4.19 +
    4.20 +	Image(const Image &img);
    4.21 +	Image &operator =(const Image &img);
    4.22 +
    4.23 +	bool create(int width, int height, unsigned char *pixels = 0);
    4.24 +	void destroy();
    4.25 +
    4.26 +	bool load(const char *fname);
    4.27 +
    4.28 +	unsigned int texture() const;
    4.29 +	int texture_width() const;
    4.30 +	int texture_height() const;
    4.31 +
    4.32 +	void invalidate_texture();
    4.33 +};
    4.34 +
    4.35 +void clear_image(Image *img);
    4.36 +void clear_image(Image *img, float r, float g, float b);
    4.37 +
    4.38 +enum ImgCombine {
    4.39 +	IMG_OP_ADD,
    4.40 +	IMG_OP_SUB,
    4.41 +	IMG_OP_MUL,
    4.42 +	IMG_OP_LERP
    4.43 +};
    4.44 +
    4.45 +bool combine_image(Image *dest, const Image *aimg, const Image *bimg, ImgCombine op = IMG_OP_LERP, float t = 0.5);
    4.46 +
    4.47 +#endif	// IMAGE_H_
     5.1 --- a/src/object.cc	Mon Jun 22 21:46:57 2015 +0300
     5.2 +++ b/src/object.cc	Thu Jun 25 05:58:35 2015 +0300
     5.3 @@ -4,6 +4,7 @@
     5.4  Object::Object()
     5.5  {
     5.6  	mesh = 0;
     5.7 +	tex = 0;
     5.8  }
     5.9  
    5.10  Object::~Object()
    5.11 @@ -31,10 +32,22 @@
    5.12  	return mesh;
    5.13  }
    5.14  
    5.15 +void Object::set_texture(unsigned int tex)
    5.16 +{
    5.17 +	this->tex = tex;
    5.18 +}
    5.19 +
    5.20  void Object::draw() const
    5.21  {
    5.22  	if(!mesh) return;
    5.23  
    5.24 +	if(tex) {
    5.25 +		glBindTexture(GL_TEXTURE_2D, tex);
    5.26 +		glEnable(GL_TEXTURE_2D);
    5.27 +	} else {
    5.28 +		glDisable(GL_TEXTURE_2D);
    5.29 +	}
    5.30 +
    5.31  	glMatrixMode(GL_MODELVIEW);
    5.32  	glPushMatrix();
    5.33  	glMultTransposeMatrixf(matrix[0]);
    5.34 @@ -42,6 +55,10 @@
    5.35  	mesh->draw();
    5.36  
    5.37  	glPopMatrix();
    5.38 +
    5.39 +	if(tex) {
    5.40 +		glDisable(GL_TEXTURE_2D);
    5.41 +	}
    5.42  }
    5.43  
    5.44  bool Object::intersect(const Ray &ray, HitPoint *hit) const
     6.1 --- a/src/object.h	Mon Jun 22 21:46:57 2015 +0300
     6.2 +++ b/src/object.h	Thu Jun 25 05:58:35 2015 +0300
     6.3 @@ -8,6 +8,7 @@
     6.4  private:
     6.5  	Mesh *mesh;
     6.6  	Matrix4x4 matrix;
     6.7 +	unsigned int tex;
     6.8  
     6.9  public:
    6.10  	Object();
    6.11 @@ -19,6 +20,8 @@
    6.12  	void set_mesh(Mesh *m);
    6.13  	Mesh *get_mesh() const;
    6.14  
    6.15 +	void set_texture(unsigned int tex);
    6.16 +
    6.17  	void draw() const;
    6.18  
    6.19  	bool intersect(const Ray &ray, HitPoint *hit) const;