Merge pull request #607 from erwincoumans/master

add TinyRenderer software renderer
This commit is contained in:
erwincoumans
2016-04-26 21:31:23 -07:00
24 changed files with 1469 additions and 12 deletions

View File

@@ -181,6 +181,7 @@
include "../examples/OpenGLWindow"
include "../examples/ThirdPartyLibs/Gwen"
include "../examples/SimpleOpenGL3"
include "../examples/TinyRenderer"
include "../examples/HelloWorld"
include "../examples/BasicDemo"

13
data/floor.mtl Normal file
View File

@@ -0,0 +1,13 @@
newmtl floor
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.0000 0.0000 0.0000
Kd 0.5880 0.5880 0.5880
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka floor_diffuse.jpg
map_Kd floor_diffuse.jpg

18
data/floor.obj Normal file
View File

@@ -0,0 +1,18 @@
o
mtllib floor.mtl
v -1 -1 -1
v 1 -1 -1
v 1 -1 1
v -1 -1 1
vt 0 0
vt 1 0
vt 1 1
vt 0 1
vn 0 1 0
usemtl floor
f 3/3/1 2/2/1 1/1/1
f 4/4/1 3/3/1 1/1/1

BIN
data/floor_diffuse.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
data/floor_diffuse.tga Normal file

Binary file not shown.

BIN
data/floor_nm_tangent.tga Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -101,7 +101,7 @@ void ImportObjSetup::initPhysics()
const char* filename = shape.material.diffuse_texname.c_str();
const unsigned char* image=0;
const char* prefix[]={"./","./data/","../data/","../../data/","../../../data/","../../../../data/"};
const char* prefix[]={ pathPrefix,"./","./data/","../data/","../../data/","../../../data/","../../../../data/"};
int numprefix = sizeof(prefix)/sizeof(const char*);
for (int i=0;!image && i<numprefix;i++)

View File

@@ -532,18 +532,13 @@ int GLInstancingRenderer::registerTexture(const unsigned char* texels, int width
b3Assert(glGetError() ==GL_NO_ERROR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width,height,0,GL_RGB,GL_UNSIGNED_BYTE,image);
b3Assert(glGetError() ==GL_NO_ERROR);
glGenerateMipmap(GL_TEXTURE_2D);
b3Assert(glGetError() ==GL_NO_ERROR);
InternalTextureHandle h;
InternalTextureHandle h;
h.m_glTexture = textureHandle;
h.m_width = width;
h.m_height = height;
m_data->m_textureHandles.push_back(h);
updateTexture(textureIndex, texels);
return textureIndex;
}
@@ -552,13 +547,31 @@ void GLInstancingRenderer::updateTexture(int textureIndex, const unsigned cha
{
if (textureIndex>=0)
{
glActiveTexture(GL_TEXTURE0);
b3Assert(glGetError() ==GL_NO_ERROR);
InternalTextureHandle& h = m_data->m_textureHandles[textureIndex];
//textures need to be flipped for OpenGL...
b3AlignedObjectArray<unsigned char> flippedTexels;
flippedTexels.resize(h.m_width* h.m_height * 3);
for (int i = 0; i < h.m_width; i++)
{
for (int j = 0; j < h.m_height; j++)
{
flippedTexels[(i + j*h.m_width) * 3] = texels[(i + (h.m_height-1-j)*h.m_width) * 3];
flippedTexels[(i + j*h.m_width) * 3+1] = texels[(i + (h.m_height - 1 - j)*h.m_width) * 3+1];
flippedTexels[(i + j*h.m_width) * 3+2] = texels[(i + (h.m_height - 1 - j)*h.m_width) * 3+1];
}
}
glBindTexture(GL_TEXTURE_2D,h.m_glTexture);
b3Assert(glGetError() ==GL_NO_ERROR);
const GLubyte* image= (const GLubyte*)texels;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, h.m_width,h.m_height,0,GL_RGB,GL_UNSIGNED_BYTE,image);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, h.m_width,h.m_height,0,GL_RGB,GL_UNSIGNED_BYTE,&flippedTexels[0]);
b3Assert(glGetError() ==GL_NO_ERROR);
glGenerateMipmap(GL_TEXTURE_2D);
b3Assert(glGetError() ==GL_NO_ERROR);

View File

@@ -461,6 +461,7 @@ void X11OpenGLWindow::enableOpenGL()
printf( "Making context current\n" );
glXMakeCurrent( m_data->m_dpy, m_data->m_win, ctx );
m_data->m_glc = ctx;
} else
{
@@ -1090,7 +1091,7 @@ int X11OpenGLWindow::fileOpenDialog(char* filename, int maxNameLength)
{
filename[len-1]=0;
printf("file open (length=%d) = %s\n", len,filename);
}
}
}
pclose(output);
} else
@@ -1099,5 +1100,5 @@ int X11OpenGLWindow::fileOpenDialog(char* filename, int maxNameLength)
}
MyXRaiseWindow(m_data->m_dpy, m_data->m_win);
return len;
}

View File

@@ -1,5 +1,5 @@
#include "OpenGLWindow/SimpleOpenGL3App.h"
#include "Bullet3Common/b3Vector3.h"
#include "Bullet3Common/b3Quaternion.h"
#include "Bullet3Common/b3CommandLineArgs.h"
#include "assert.h"
#include <stdio.h>
@@ -92,6 +92,15 @@ int main(int argc, char* argv[])
int textureHandle = app->m_renderer->registerTexture(image,textureWidth,textureHeight);
int cubeIndex = app->registerCubeShape(1,1,1);
b3Vector3 pos = b3MakeVector3(0,0,0);
b3Quaternion orn(0,0,0,1);
b3Vector3 color=b3MakeVector3(1,0,0);
b3Vector3 scaling=b3MakeVector3 (1,1,1);
app->m_renderer->registerGraphicsInstance(cubeIndex,pos,orn,color,scaling);
app->m_renderer->writeTransforms();
do
{
@@ -131,6 +140,7 @@ int main(int argc, char* argv[])
app->m_instancingRenderer->init();
app->m_instancingRenderer->updateCamera();
app->m_renderer->renderScene();
app->drawGrid();
char bla[1024];
sprintf(bla,"Simple test frame %d", frameCount);

View File

@@ -0,0 +1,13 @@
Tiny Renderer, https://github.com/ssloy/tinyrenderer
Copyright Dmitry V. Sokolov
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

View File

@@ -0,0 +1,172 @@
#include "TinyRenderer.h"
#include <vector>
#include <limits>
#include <iostream>
#include "TinyRenderer/tgaimage.h"
#include "TinyRenderer/model.h"
#include "TinyRenderer/geometry.h"
#include "TinyRenderer/our_gl.h"
#include "../Utils/b3ResourcePath.h"
#include "Bullet3Common/b3MinMax.h"
Vec3f light_dir_world(1,1,1);
struct Shader : public IShader {
Model* m_model;
Vec3f m_light_dir_local;
Matrix& m_modelView;
Matrix& m_projectionMatrix;
mat<2,3,float> varying_uv; // triangle uv coordinates, written by the vertex shader, read by the fragment shader
mat<4,3,float> varying_tri; // triangle coordinates (clip coordinates), written by VS, read by FS
mat<3,3,float> varying_nrm; // normal per vertex to be interpolated by FS
mat<3,3,float> ndc_tri; // triangle in normalized device coordinates
Shader(Model* model, Vec3f light_dir_local, Matrix& modelView, Matrix& projectionMatrix)
:m_model(model),
m_light_dir_local(light_dir_local),
m_modelView(modelView),
m_projectionMatrix(projectionMatrix)
{
}
virtual Vec4f vertex(int iface, int nthvert) {
varying_uv.set_col(nthvert, m_model->uv(iface, nthvert));
varying_nrm.set_col(nthvert, proj<3>((m_projectionMatrix*m_modelView).invert_transpose()*embed<4>(m_model->normal(iface, nthvert), 0.f)));
Vec4f gl_Vertex = m_projectionMatrix*m_modelView*embed<4>(m_model->vert(iface, nthvert));
varying_tri.set_col(nthvert, gl_Vertex);
ndc_tri.set_col(nthvert, proj<3>(gl_Vertex/gl_Vertex[3]));
return gl_Vertex;
}
virtual bool fragment(Vec3f bar, TGAColor &color) {
Vec3f bn = (varying_nrm*bar).normalize();
Vec2f uv = varying_uv*bar;
mat<3,3,float> A;
A[0] = ndc_tri.col(1) - ndc_tri.col(0);
A[1] = ndc_tri.col(2) - ndc_tri.col(0);
A[2] = bn;
mat<3,3,float> AI = A.invert();
Vec3f i = AI * Vec3f(varying_uv[0][1] - varying_uv[0][0], varying_uv[0][2] - varying_uv[0][0], 0);
Vec3f j = AI * Vec3f(varying_uv[1][1] - varying_uv[1][0], varying_uv[1][2] - varying_uv[1][0], 0);
mat<3,3,float> B;
B.set_col(0, i.normalize());
B.set_col(1, j.normalize());
B.set_col(2, bn);
Vec3f n = (B*m_model->normal(uv)).normalize();
float diff = b3Min(b3Max(0.f, n*m_light_dir_local+0.6f),1.f);
color = m_model->diffuse(uv)*diff;
return false;
}
};
/*
struct TinyRenderObjectData
{
//Camera
Matrix m_viewMatrix;
Matrix m_projectionMatrix;
Matrix m_viewPortMatrix;
//Model (vertices, indices, textures, shader)
Matrix m_modelMatrix;
class Model* m_model;
class IShader* m_shader;
//Output
TGAImage m_rgbColorBuffer;
b3AlignedObjectArray<float> m_depthBuffer;
};
*/
TinyRenderObjectData::TinyRenderObjectData(int width, int height, const char* fileName)
:m_width(width),
m_height(height),
m_rgbColorBuffer(width,height,TGAImage::RGB),
m_model(0)
{
Vec3f eye(1,1,3);
Vec3f center(0,0,0);
Vec3f up(0,1,0);
m_viewMatrix = lookat(eye, center, up);
m_viewportMatrix = viewport(width/8, height/8, width*3/4, height*3/4);
m_projectionMatrix = projection(-1.f/(eye-center).norm());
m_depthBuffer.resize(width*height);
//todo(erwincoumans) move the file loading out of here
char relativeFileName[1024];
if (!b3ResourcePath::findResourcePath(fileName, relativeFileName, 1024))
{
printf("Cannot find file %s\n", fileName);
} else
{
m_model = new Model(relativeFileName);
}
}
TinyRenderObjectData::~TinyRenderObjectData()
{
delete m_model;
}
void TinyRenderer::renderObject(TinyRenderObjectData& renderData)
{
const char* fileName = "obj/floor.obj";
//new Model(relativeFileName);//argv[m]);
Model* model = renderData.m_model;
if (0==model)
return;
const int width = renderData.m_width;
const int height = renderData.m_height;
b3AlignedObjectArray<float>& zbuffer = renderData.m_depthBuffer;
//todo(erwincoumans) make this a separate call
for (int i=width*height; i--; zbuffer[i] = -std::numeric_limits<float>::max());
TGAImage& frame = renderData.m_rgbColorBuffer;
//lookat(eye, center, up);
//viewport(width/8, height/8, width*3/4, height*3/4);
//projection(-1.f/(eye-center).norm());
Vec3f light_dir_local = proj<3>((renderData.m_projectionMatrix*renderData.m_viewMatrix*embed<4>(light_dir_world, 0.f))).normalize();
{
//for (int m=1; m<argc; m++) {
Shader shader(model, light_dir_local, renderData.m_viewMatrix, renderData.m_projectionMatrix);
for (int i=0; i<model->nfaces(); i++) {
for (int j=0; j<3; j++) {
shader.vertex(i, j);
}
triangle(shader.varying_tri, shader, frame, &zbuffer[0], renderData.m_viewportMatrix);
}
}
//frame.flip_vertically(); // to place the origin in the bottom left corner of the image
//frame.write_tga_file("framebuffer.tga");
}

View File

@@ -0,0 +1,39 @@
#ifndef TINY_RENDERER_H
#define TINY_RENDERER_H
//#include "TinyRenderer/our_gl.h"
#include "TinyRenderer/geometry.h"
#include "Bullet3Common/b3AlignedObjectArray.h"
#include "TinyRenderer/tgaimage.h"
struct TinyRenderObjectData
{
//Camera
Matrix m_viewMatrix;
Matrix m_projectionMatrix;
Matrix m_viewportMatrix;
//Model (vertices, indices, textures, shader)
Matrix m_modelMatrix;
class Model* m_model;
//class IShader* m_shader; todo(erwincoumans) expose the shader, for now we use a default shader
//Output
int m_width;
int m_height;
TGAImage m_rgbColorBuffer;
b3AlignedObjectArray<float> m_depthBuffer;
TinyRenderObjectData(int width, int height, const char* objFileName);
virtual ~TinyRenderObjectData();
};
class TinyRenderer
{
public:
static void renderObject(TinyRenderObjectData& renderData);
};
#endif // TINY_RENDERER_Hbla

View File

@@ -0,0 +1,7 @@
#include "geometry.h"
template <> template <> vec<3,int> ::vec(const vec<3,float> &v) : x(int(v.x+.5f)),y(int(v.y+.5f)),z(int(v.z+.5f)) {}
template <> template <> vec<3,float>::vec(const vec<3,int> &v) : x(v.x),y(v.y),z(v.z) {}
template <> template <> vec<2,int> ::vec(const vec<2,float> &v) : x(int(v.x+.5f)),y(int(v.y+.5f)) {}
template <> template <> vec<2,float>::vec(const vec<2,int> &v) : x(v.x),y(v.y) {}

View File

@@ -0,0 +1,222 @@
#ifndef __GEOMETRY_H__
#define __GEOMETRY_H__
#include <cmath>
#include <cassert>
#include <stdlib.h>
template<size_t DimCols,size_t DimRows,typename T> class mat;
template <size_t DIM, typename T> struct vec {
vec() { for (size_t i=DIM; i--; data_[i] = T()); }
T& operator[](const size_t i) { assert(i<DIM); return data_[i]; }
const T& operator[](const size_t i) const { assert(i<DIM); return data_[i]; }
private:
T data_[DIM];
};
/////////////////////////////////////////////////////////////////////////////////
template <typename T> struct vec<2,T> {
vec() : x(T()), y(T()) {}
vec(T X, T Y) : x(X), y(Y) {}
template <class U> vec<2,T>(const vec<2,U> &v);
T& operator[](const size_t i) { assert(i<2); return i<=0 ? x : y; }
const T& operator[](const size_t i) const { assert(i<2); return i<=0 ? x : y; }
T x,y;
};
/////////////////////////////////////////////////////////////////////////////////
template <typename T> struct vec<3,T> {
vec() : x(T()), y(T()), z(T()) {}
vec(T X, T Y, T Z) : x(X), y(Y), z(Z) {}
template <class U> vec<3,T>(const vec<3,U> &v);
T& operator[](const size_t i) { assert(i<3); return i<=0 ? x : (1==i ? y : z); }
const T& operator[](const size_t i) const { assert(i<3); return i<=0 ? x : (1==i ? y : z); }
float norm() { return std::sqrt(x*x+y*y+z*z); }
vec<3,T> & normalize(T l=1) { *this = (*this)*(l/norm()); return *this; }
T x,y,z;
};
/////////////////////////////////////////////////////////////////////////////////
template<size_t DIM,typename T> T operator*(const vec<DIM,T>& lhs, const vec<DIM,T>& rhs) {
T ret = T();
for (size_t i=DIM; i--; ret+=lhs[i]*rhs[i]);
return ret;
}
template<size_t DIM,typename T>vec<DIM,T> operator+(vec<DIM,T> lhs, const vec<DIM,T>& rhs) {
for (size_t i=DIM; i--; lhs[i]+=rhs[i]);
return lhs;
}
template<size_t DIM,typename T>vec<DIM,T> operator-(vec<DIM,T> lhs, const vec<DIM,T>& rhs) {
for (size_t i=DIM; i--; lhs[i]-=rhs[i]);
return lhs;
}
template<size_t DIM,typename T,typename U> vec<DIM,T> operator*(vec<DIM,T> lhs, const U& rhs) {
for (size_t i=DIM; i--; lhs[i]*=rhs);
return lhs;
}
template<size_t DIM,typename T,typename U> vec<DIM,T> operator/(vec<DIM,T> lhs, const U& rhs) {
for (size_t i=DIM; i--; lhs[i]/=rhs);
return lhs;
}
template<size_t LEN,size_t DIM,typename T> vec<LEN,T> embed(const vec<DIM,T> &v, T fill=1) {
vec<LEN,T> ret;
for (size_t i=LEN; i--; ret[i]=(i<DIM?v[i]:fill));
return ret;
}
template<size_t LEN,size_t DIM, typename T> vec<LEN,T> proj(const vec<DIM,T> &v) {
vec<LEN,T> ret;
for (size_t i=LEN; i--; ret[i]=v[i]);
return ret;
}
template <typename T> vec<3,T> cross(vec<3,T> v1, vec<3,T> v2) {
return vec<3,T>(v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x);
}
#if 0
template <size_t DIM, typename T> std::ostream& operator<<(std::ostream& out, vec<DIM,T>& v) {
for(unsigned int i=0; i<DIM; i++) {
out << v[i] << " " ;
}
return out ;
}
#endif
/////////////////////////////////////////////////////////////////////////////////
template<size_t DIM,typename T> struct dt {
static T det(const mat<DIM,DIM,T>& src) {
T ret=0;
for (size_t i=DIM; i--; ret += src[0][i]*src.cofactor(0,i));
return ret;
}
};
template<typename T> struct dt<1,T> {
static T det(const mat<1,1,T>& src) {
return src[0][0];
}
};
/////////////////////////////////////////////////////////////////////////////////
template<size_t DimRows,size_t DimCols,typename T> class mat {
vec<DimCols,T> rows[DimRows];
public:
mat() {}
vec<DimCols,T>& operator[] (const size_t idx) {
assert(idx<DimRows);
return rows[idx];
}
const vec<DimCols,T>& operator[] (const size_t idx) const {
assert(idx<DimRows);
return rows[idx];
}
vec<DimRows,T> col(const size_t idx) const {
assert(idx<DimCols);
vec<DimRows,T> ret;
for (size_t i=DimRows; i--; ret[i]=rows[i][idx]);
return ret;
}
void set_col(size_t idx, vec<DimRows,T> v) {
assert(idx<DimCols);
for (size_t i=DimRows; i--; rows[i][idx]=v[i]);
}
static mat<DimRows,DimCols,T> identity() {
mat<DimRows,DimCols,T> ret;
for (size_t i=DimRows; i--; )
for (size_t j=DimCols;j--; ret[i][j]=(i==j));
return ret;
}
T det() const {
return dt<DimCols,T>::det(*this);
}
mat<DimRows-1,DimCols-1,T> get_minor(size_t row, size_t col) const {
mat<DimRows-1,DimCols-1,T> ret;
for (size_t i=DimRows-1; i--; )
for (size_t j=DimCols-1;j--; ret[i][j]=rows[i<row?i:i+1][j<col?j:j+1]);
return ret;
}
T cofactor(size_t row, size_t col) const {
return get_minor(row,col).det()*((row+col)%2 ? -1 : 1);
}
mat<DimRows,DimCols,T> adjugate() const {
mat<DimRows,DimCols,T> ret;
for (size_t i=DimRows; i--; )
for (size_t j=DimCols; j--; ret[i][j]=cofactor(i,j));
return ret;
}
mat<DimRows,DimCols,T> invert_transpose() {
mat<DimRows,DimCols,T> ret = adjugate();
T tmp = ret[0]*rows[0];
return ret/tmp;
}
mat<DimRows,DimCols,T> invert() {
return invert_transpose().transpose();
}
mat<DimCols,DimRows,T> transpose() {
mat<DimCols,DimRows,T> ret;
for (size_t i=DimCols; i--; ret[i]=this->col(i));
return ret;
}
};
/////////////////////////////////////////////////////////////////////////////////
template<size_t DimRows,size_t DimCols,typename T> vec<DimRows,T> operator*(const mat<DimRows,DimCols,T>& lhs, const vec<DimCols,T>& rhs) {
vec<DimRows,T> ret;
for (size_t i=DimRows; i--; ret[i]=lhs[i]*rhs);
return ret;
}
template<size_t R1,size_t C1,size_t C2,typename T>mat<R1,C2,T> operator*(const mat<R1,C1,T>& lhs, const mat<C1,C2,T>& rhs) {
mat<R1,C2,T> result;
for (size_t i=R1; i--; )
for (size_t j=C2; j--; result[i][j]=lhs[i]*rhs.col(j));
return result;
}
template<size_t DimRows,size_t DimCols,typename T>mat<DimCols,DimRows,T> operator/(mat<DimRows,DimCols,T> lhs, const T& rhs) {
for (size_t i=DimRows; i--; lhs[i]=lhs[i]/rhs);
return lhs;
}
#if 0
template <size_t DimRows,size_t DimCols,class T> std::ostream& operator<<(std::ostream& out, mat<DimRows,DimCols,T>& m) {
for (size_t i=0; i<DimRows; i++) out << m[i] << std::endl;
return out;
}
#endif
/////////////////////////////////////////////////////////////////////////////////
typedef vec<2, float> Vec2f;
typedef vec<2, int> Vec2i;
typedef vec<3, float> Vec3f;
typedef vec<3, int> Vec3i;
typedef vec<4, float> Vec4f;
typedef mat<4,4,float> Matrix;
#endif //__GEOMETRY_H__

View File

@@ -0,0 +1,209 @@
#include "OpenGLWindow/SimpleOpenGL3App.h"
#include "Bullet3Common/b3Quaternion.h"
#include "Bullet3Common/b3CommandLineArgs.h"
#include "assert.h"
#include <stdio.h>
char* gVideoFileName = 0;
char* gPngFileName = 0;
static b3WheelCallback sOldWheelCB = 0;
static b3ResizeCallback sOldResizeCB = 0;
static b3MouseMoveCallback sOldMouseMoveCB = 0;
static b3MouseButtonCallback sOldMouseButtonCB = 0;
static b3KeyboardCallback sOldKeyboardCB = 0;
//static b3RenderCallback sOldRenderCB = 0;
float gWidth = 0 ;
float gHeight = 0;
void MyWheelCallback(float deltax, float deltay)
{
if (sOldWheelCB)
sOldWheelCB(deltax,deltay);
}
void MyResizeCallback( float width, float height)
{
gWidth = width;
gHeight = height;
if (sOldResizeCB)
sOldResizeCB(width,height);
}
void MyMouseMoveCallback( float x, float y)
{
printf("Mouse Move: %f, %f\n", x,y);
if (sOldMouseMoveCB)
sOldMouseMoveCB(x,y);
}
void MyMouseButtonCallback(int button, int state, float x, float y)
{
if (sOldMouseButtonCB)
sOldMouseButtonCB(button,state,x,y);
}
void MyKeyboardCallback(int keycode, int state)
{
//keycodes are in examples/CommonInterfaces/CommonWindowInterface.h
//for example B3G_ESCAPE for escape key
//state == 1 for pressed, state == 0 for released.
// use app->m_window->isModifiedPressed(...) to check for shift, escape and alt keys
printf("MyKeyboardCallback received key:%c in state %d\n",keycode,state);
if (sOldKeyboardCB)
sOldKeyboardCB(keycode,state);
}
#include "TinyRenderer.h"
int main(int argc, char* argv[])
{
b3CommandLineArgs myArgs(argc,argv);
SimpleOpenGL3App* app = new SimpleOpenGL3App("SimpleOpenGL3App",640,480,true);
app->m_instancingRenderer->getActiveCamera()->setCameraDistance(13);
app->m_instancingRenderer->getActiveCamera()->setCameraPitch(0);
app->m_instancingRenderer->getActiveCamera()->setCameraTargetPosition(0,0,0);
sOldKeyboardCB = app->m_window->getKeyboardCallback();
app->m_window->setKeyboardCallback(MyKeyboardCallback);
sOldMouseMoveCB = app->m_window->getMouseMoveCallback();
app->m_window->setMouseMoveCallback(MyMouseMoveCallback);
sOldMouseButtonCB = app->m_window->getMouseButtonCallback();
app->m_window->setMouseButtonCallback(MyMouseButtonCallback);
sOldWheelCB = app->m_window->getWheelCallback();
app->m_window->setWheelCallback(MyWheelCallback);
sOldResizeCB = app->m_window->getResizeCallback();
app->m_window->setResizeCallback(MyResizeCallback);
int textureWidth = gWidth;
int textureHeight = gHeight;
TinyRenderObjectData renderData(textureWidth, textureHeight, "floor.obj");
myArgs.GetCmdLineArgument("mp4_file",gVideoFileName);
if (gVideoFileName)
app->dumpFramesToVideo(gVideoFileName);
myArgs.GetCmdLineArgument("png_file",gPngFileName);
char fileName[1024];
unsigned char* image=new unsigned char[textureWidth*textureHeight*4];
int textureHandle = app->m_renderer->registerTexture(image,textureWidth,textureHeight);
int cubeIndex = app->registerCubeShape(1,1,1);
b3Vector3 pos = b3MakeVector3(0,0,0);
b3Quaternion orn(0,0,0,1);
b3Vector3 color=b3MakeVector3(1,0,0);
b3Vector3 scaling=b3MakeVector3 (1,1,1);
app->m_renderer->registerGraphicsInstance(cubeIndex,pos,orn,color,scaling);
app->m_renderer->writeTransforms();
do
{
static int frameCount = 0;
frameCount++;
if (gPngFileName)
{
printf("gPngFileName=%s\n",gPngFileName);
sprintf(fileName,"%s%d.png",gPngFileName,frameCount++);
app->dumpNextFrameToPng(fileName);
}
app->m_instancingRenderer->init();
app->m_instancingRenderer->updateCamera();
float projMat[16];
app->m_instancingRenderer->getActiveCamera()->getCameraProjectionMatrix(projMat);
float viewMat[16];
app->m_instancingRenderer->getActiveCamera()->getCameraViewMatrix(viewMat);
for (int i=0;i<4;i++)
{
for (int j=0;j<4;j++)
{
renderData.m_viewMatrix[i][j] = viewMat[i+4*j];
//renderData.m_projectionMatrix[i][j] = projMat[i+4*j];
}
}
for(int y=0;y<textureHeight;++y)
{
unsigned char* pi=image+(y)*textureWidth*3;
for(int x=0;x<textureWidth;++x)
{
TGAColor color;
color.bgra[0] = 255;
color.bgra[1] = 255;
color.bgra[2] = 255;
color.bgra[3] = 255;
renderData.m_rgbColorBuffer.set(x,y,color);
}
}
TinyRenderer::renderObject(renderData);
#if 1
//update the texels of the texture using a simple pattern, animated using frame index
for(int y=0;y<textureHeight;++y)
{
unsigned char* pi=image+(y)*textureWidth*3;
for(int x=0;x<textureWidth;++x)
{
TGAColor color = renderData.m_rgbColorBuffer.get(x,y);
pi[0] = color.bgra[2];
pi[1] = color.bgra[1];
pi[2] = color.bgra[0];
pi+=3;
}
}
#else
//update the texels of the texture using a simple pattern, animated using frame index
for(int y=0;y<textureHeight;++y)
{
const int t=(y+frameCount)>>4;
unsigned char* pi=image+y*textureWidth*3;
for(int x=0;x<textureWidth;++x)
{
const int s=x>>4;
const unsigned char b=180;
unsigned char c=b+((s+(t&1))&1)*(255-b);
pi[0]=pi[1]=pi[2]=pi[3]=c;pi+=3;
}
}
#endif
app->m_renderer->activateTexture(textureHandle);
app->m_renderer->updateTexture(textureHandle,image);
float color[4] = {1,1,1,1};
app->m_primRenderer->drawTexturedRect(0,0,gWidth/3,gHeight/3,color,0,0,1,1,true);
app->m_renderer->renderScene();
app->drawGrid();
char bla[1024];
sprintf(bla,"Simple test frame %d", frameCount);
app->drawText(bla,10,10);
app->swapBuffer();
} while (!app->m_window->requestedExit());
delete app;
return 0;
}

View File

@@ -0,0 +1,108 @@
#include <iostream>
#include <fstream>
#include <sstream>
#include "model.h"
Model::Model(const char *filename) : verts_(), faces_(), norms_(), uv_(), diffusemap_(), normalmap_(), specularmap_() {
std::ifstream in;
in.open (filename, std::ifstream::in);
if (in.fail()) return;
std::string line;
while (!in.eof()) {
std::getline(in, line);
std::istringstream iss(line.c_str());
char trash;
if (!line.compare(0, 2, "v ")) {
iss >> trash;
Vec3f v;
for (int i=0;i<3;i++) iss >> v[i];
verts_.push_back(v);
} else if (!line.compare(0, 3, "vn ")) {
iss >> trash >> trash;
Vec3f n;
for (int i=0;i<3;i++) iss >> n[i];
norms_.push_back(n);
} else if (!line.compare(0, 3, "vt ")) {
iss >> trash >> trash;
Vec2f uv;
for (int i=0;i<2;i++) iss >> uv[i];
uv_.push_back(uv);
} else if (!line.compare(0, 2, "f ")) {
std::vector<Vec3i> f;
Vec3i tmp;
iss >> trash;
while (iss >> tmp[0] >> trash >> tmp[1] >> trash >> tmp[2]) {
for (int i=0; i<3; i++) tmp[i]--; // in wavefront obj all indices start at 1, not zero
f.push_back(tmp);
}
faces_.push_back(f);
}
}
std::cerr << "# v# " << verts_.size() << " f# " << faces_.size() << " vt# " << uv_.size() << " vn# " << norms_.size() << std::endl;
load_texture(filename, "_diffuse.tga", diffusemap_);
load_texture(filename, "_nm_tangent.tga", normalmap_);
load_texture(filename, "_spec.tga", specularmap_);
}
Model::~Model() {}
int Model::nverts() {
return (int)verts_.size();
}
int Model::nfaces() {
return (int)faces_.size();
}
std::vector<int> Model::face(int idx) {
std::vector<int> face;
for (int i=0; i<(int)faces_[idx].size(); i++) face.push_back(faces_[idx][i][0]);
return face;
}
Vec3f Model::vert(int i) {
return verts_[i];
}
Vec3f Model::vert(int iface, int nthvert) {
return verts_[faces_[iface][nthvert][0]];
}
void Model::load_texture(std::string filename, const char *suffix, TGAImage &img) {
std::string texfile(filename);
size_t dot = texfile.find_last_of(".");
if (dot!=std::string::npos) {
texfile = texfile.substr(0,dot) + std::string(suffix);
std::cerr << "texture file " << texfile << " loading " << (img.read_tga_file(texfile.c_str()) ? "ok" : "failed") << std::endl;
img.flip_vertically();
}
}
TGAColor Model::diffuse(Vec2f uvf) {
Vec2i uv(uvf[0]*diffusemap_.get_width(), uvf[1]*diffusemap_.get_height());
return diffusemap_.get(uv[0], uv[1]);
}
Vec3f Model::normal(Vec2f uvf) {
Vec2i uv(uvf[0]*normalmap_.get_width(), uvf[1]*normalmap_.get_height());
TGAColor c = normalmap_.get(uv[0], uv[1]);
Vec3f res;
for (int i=0; i<3; i++)
res[2-i] = (float)c[i]/255.f*2.f - 1.f;
return res;
}
Vec2f Model::uv(int iface, int nthvert) {
return uv_[faces_[iface][nthvert][1]];
}
float Model::specular(Vec2f uvf) {
Vec2i uv(uvf[0]*specularmap_.get_width(), uvf[1]*specularmap_.get_height());
return specularmap_.get(uv[0], uv[1])[0]/1.f;
}
Vec3f Model::normal(int iface, int nthvert) {
int idx = faces_[iface][nthvert][2];
return norms_[idx].normalize();
}

View File

@@ -0,0 +1,33 @@
#ifndef __MODEL_H__
#define __MODEL_H__
#include <vector>
#include <string>
#include "geometry.h"
#include "tgaimage.h"
class Model {
private:
std::vector<Vec3f> verts_;
std::vector<std::vector<Vec3i> > faces_; // attention, this Vec3i means vertex/uv/normal
std::vector<Vec3f> norms_;
std::vector<Vec2f> uv_;
TGAImage diffusemap_;
TGAImage normalmap_;
TGAImage specularmap_;
void load_texture(std::string filename, const char *suffix, TGAImage &img);
public:
Model(const char *filename);
~Model();
int nverts();
int nfaces();
Vec3f normal(int iface, int nthvert);
Vec3f normal(Vec2f uv);
Vec3f vert(int i);
Vec3f vert(int iface, int nthvert);
Vec2f uv(int iface, int nthvert);
TGAColor diffuse(Vec2f uv);
float specular(Vec2f uv);
std::vector<int> face(int idx);
};
#endif //__MODEL_H__

View File

@@ -0,0 +1,93 @@
#include <cmath>
#include <limits>
#include <cstdlib>
#include "our_gl.h"
#include "Bullet3Common/b3MinMax.h"
IShader::~IShader() {}
Matrix viewport(int x, int y, int w, int h)
{
Matrix Viewport;
Viewport = Matrix::identity();
Viewport[0][3] = x+w/2.f;
Viewport[1][3] = y+h/2.f;
Viewport[2][3] = 1.f;
Viewport[0][0] = w/2.f;
Viewport[1][1] = h/2.f;
Viewport[2][2] = 0;
return Viewport;
}
Matrix projection(float coeff) {
Matrix Projection;
Projection = Matrix::identity();
Projection[3][2] = coeff;
return Projection;
}
Matrix lookat(Vec3f eye, Vec3f center, Vec3f up) {
Vec3f z = (eye-center).normalize();
Vec3f x = cross(up,z).normalize();
Vec3f y = cross(z,x).normalize();
Matrix Minv = Matrix::identity();
Matrix Tr = Matrix::identity();
for (int i=0; i<3; i++) {
Minv[0][i] = x[i];
Minv[1][i] = y[i];
Minv[2][i] = z[i];
Tr[i][3] = -center[i];
}
Matrix ModelView;
ModelView = Minv*Tr;
return ModelView;
}
Vec3f barycentric(Vec2f A, Vec2f B, Vec2f C, Vec2f P) {
Vec3f s[2];
for (int i=2; i--; ) {
s[i][0] = C[i]-A[i];
s[i][1] = B[i]-A[i];
s[i][2] = A[i]-P[i];
}
Vec3f u = cross(s[0], s[1]);
if (std::abs(u[2])>1e-2) // dont forget that u[2] is integer. If it is zero then triangle ABC is degenerate
return Vec3f(1.f-(u.x+u.y)/u.z, u.y/u.z, u.x/u.z);
return Vec3f(-1,1,1); // in this case generate negative coordinates, it will be thrown away by the rasterizator
}
void triangle(mat<4,3,float> &clipc, IShader &shader, TGAImage &image, float *zbuffer, const Matrix& viewPortMatrix) {
mat<3,4,float> pts = (viewPortMatrix*clipc).transpose(); // transposed to ease access to each of the points
mat<3,2,float> pts2;
for (int i=0; i<3; i++) pts2[i] = proj<2>(pts[i]/pts[i][3]);
Vec2f bboxmin( std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
Vec2f bboxmax(-std::numeric_limits<float>::max(), -std::numeric_limits<float>::max());
Vec2f clamp(image.get_width()-1, image.get_height()-1);
for (int i=0; i<3; i++) {
for (int j=0; j<2; j++) {
bboxmin[j] = b3Max(0.f, b3Min(bboxmin[j], pts2[i][j]));
bboxmax[j] = b3Min(clamp[j], b3Max(bboxmax[j], pts2[i][j]));
}
}
Vec2i P;
TGAColor color;
for (P.x=bboxmin.x; P.x<=bboxmax.x; P.x++) {
for (P.y=bboxmin.y; P.y<=bboxmax.y; P.y++) {
Vec3f bc_screen = barycentric(pts2[0], pts2[1], pts2[2], P);
Vec3f bc_clip = Vec3f(bc_screen.x/pts[0][3], bc_screen.y/pts[1][3], bc_screen.z/pts[2][3]);
bc_clip = bc_clip/(bc_clip.x+bc_clip.y+bc_clip.z);
float frag_depth = clipc[2]*bc_clip;
if (bc_screen.x<0 || bc_screen.y<0 || bc_screen.z<0 || zbuffer[P.x+P.y*image.get_width()]>frag_depth) continue;
bool discard = shader.fragment(bc_clip, color);
if (!discard) {
zbuffer[P.x+P.y*image.get_width()] = frag_depth;
image.set(P.x, P.y, color);
}
}
}
}

View File

@@ -0,0 +1,22 @@
#ifndef __OUR_GL_H__
#define __OUR_GL_H__
#include "tgaimage.h"
#include "geometry.h"
Matrix viewport(int x, int y, int w, int h);
Matrix projection(float coeff=0.f); // coeff = -1/c
Matrix lookat(Vec3f eye, Vec3f center, Vec3f up);
struct IShader {
virtual ~IShader();
virtual Vec4f vertex(int iface, int nthvert) = 0;
virtual bool fragment(Vec3f bar, TGAColor &color) = 0;
};
//void triangle(Vec4f *pts, IShader &shader, TGAImage &image, float *zbuffer);
void triangle(mat<4,3,float> &pts, IShader &shader, TGAImage &image, float *zbuffer, const Matrix& viewPortMatrix);
#endif //__OUR_GL_H__

View File

@@ -0,0 +1,29 @@
project "App_TinyRenderer"
language "C++"
kind "ConsoleApp"
includedirs {
".",
"../../src",
".."
}
links{ "OpenGL_Window","Bullet3Common"}
initOpenGL()
initGlew()
files {
"*.cpp",
"*.h",
"../Utils/b3ResourcePath.cpp"
}
if os.is("Linux") then initX11() end
if os.is("MacOSX") then
links{"Cocoa.framework"}
end

View File

@@ -0,0 +1,356 @@
#include <iostream>
#include <fstream>
#include <string.h>
#include <time.h>
#include <math.h>
#include "tgaimage.h"
TGAImage::TGAImage() : data(NULL), width(0), height(0), bytespp(0) {}
TGAImage::TGAImage(int w, int h, int bpp) : data(NULL), width(w), height(h), bytespp(bpp) {
unsigned long nbytes = width*height*bytespp;
data = new unsigned char[nbytes];
memset(data, 0, nbytes);
}
TGAImage::TGAImage(const TGAImage &img) : data(NULL), width(img.width), height(img.height), bytespp(img.bytespp) {
unsigned long nbytes = width*height*bytespp;
data = new unsigned char[nbytes];
memcpy(data, img.data, nbytes);
}
TGAImage::~TGAImage() {
if (data) delete [] data;
}
TGAImage & TGAImage::operator =(const TGAImage &img) {
if (this != &img) {
if (data) delete [] data;
width = img.width;
height = img.height;
bytespp = img.bytespp;
unsigned long nbytes = width*height*bytespp;
data = new unsigned char[nbytes];
memcpy(data, img.data, nbytes);
}
return *this;
}
bool TGAImage::read_tga_file(const char *filename) {
if (data) delete [] data;
data = NULL;
std::ifstream in;
in.open (filename, std::ios::binary);
if (!in.is_open()) {
std::cerr << "can't open file " << filename << "\n";
in.close();
return false;
}
TGA_Header header;
in.read((char *)&header, sizeof(header));
if (!in.good()) {
in.close();
std::cerr << "an error occured while reading the header\n";
return false;
}
width = header.width;
height = header.height;
bytespp = header.bitsperpixel>>3;
if (width<=0 || height<=0 || (bytespp!=GRAYSCALE && bytespp!=RGB && bytespp!=RGBA)) {
in.close();
std::cerr << "bad bpp (or width/height) value\n";
return false;
}
unsigned long nbytes = bytespp*width*height;
data = new unsigned char[nbytes];
if (3==header.datatypecode || 2==header.datatypecode) {
in.read((char *)data, nbytes);
if (!in.good()) {
in.close();
std::cerr << "an error occured while reading the data\n";
return false;
}
} else if (10==header.datatypecode||11==header.datatypecode) {
if (!load_rle_data(in)) {
in.close();
std::cerr << "an error occured while reading the data\n";
return false;
}
} else {
in.close();
std::cerr << "unknown file format " << (int)header.datatypecode << "\n";
return false;
}
if (!(header.imagedescriptor & 0x20)) {
flip_vertically();
}
if (header.imagedescriptor & 0x10) {
flip_horizontally();
}
std::cerr << width << "x" << height << "/" << bytespp*8 << "\n";
in.close();
return true;
}
bool TGAImage::load_rle_data(std::ifstream &in) {
unsigned long pixelcount = width*height;
unsigned long currentpixel = 0;
unsigned long currentbyte = 0;
TGAColor colorbuffer;
do {
unsigned char chunkheader = 0;
chunkheader = in.get();
if (!in.good()) {
std::cerr << "an error occured while reading the data\n";
return false;
}
if (chunkheader<128) {
chunkheader++;
for (int i=0; i<chunkheader; i++) {
in.read((char *)colorbuffer.bgra, bytespp);
if (!in.good()) {
std::cerr << "an error occured while reading the header\n";
return false;
}
for (int t=0; t<bytespp; t++)
data[currentbyte++] = colorbuffer.bgra[t];
currentpixel++;
if (currentpixel>pixelcount) {
std::cerr << "Too many pixels read\n";
return false;
}
}
} else {
chunkheader -= 127;
in.read((char *)colorbuffer.bgra, bytespp);
if (!in.good()) {
std::cerr << "an error occured while reading the header\n";
return false;
}
for (int i=0; i<chunkheader; i++) {
for (int t=0; t<bytespp; t++)
data[currentbyte++] = colorbuffer.bgra[t];
currentpixel++;
if (currentpixel>pixelcount) {
std::cerr << "Too many pixels read\n";
return false;
}
}
}
} while (currentpixel < pixelcount);
return true;
}
bool TGAImage::write_tga_file(const char *filename, bool rle) {
unsigned char developer_area_ref[4] = {0, 0, 0, 0};
unsigned char extension_area_ref[4] = {0, 0, 0, 0};
unsigned char footer[18] = {'T','R','U','E','V','I','S','I','O','N','-','X','F','I','L','E','.','\0'};
std::ofstream out;
out.open (filename, std::ios::binary);
if (!out.is_open()) {
std::cerr << "can't open file " << filename << "\n";
out.close();
return false;
}
TGA_Header header;
memset((void *)&header, 0, sizeof(header));
header.bitsperpixel = bytespp<<3;
header.width = width;
header.height = height;
header.datatypecode = (bytespp==GRAYSCALE?(rle?11:3):(rle?10:2));
header.imagedescriptor = 0x20; // top-left origin
out.write((char *)&header, sizeof(header));
if (!out.good()) {
out.close();
std::cerr << "can't dump the tga file\n";
return false;
}
if (!rle) {
out.write((char *)data, width*height*bytespp);
if (!out.good()) {
std::cerr << "can't unload raw data\n";
out.close();
return false;
}
} else {
if (!unload_rle_data(out)) {
out.close();
std::cerr << "can't unload rle data\n";
return false;
}
}
out.write((char *)developer_area_ref, sizeof(developer_area_ref));
if (!out.good()) {
std::cerr << "can't dump the tga file\n";
out.close();
return false;
}
out.write((char *)extension_area_ref, sizeof(extension_area_ref));
if (!out.good()) {
std::cerr << "can't dump the tga file\n";
out.close();
return false;
}
out.write((char *)footer, sizeof(footer));
if (!out.good()) {
std::cerr << "can't dump the tga file\n";
out.close();
return false;
}
out.close();
return true;
}
// TODO: it is not necessary to break a raw chunk for two equal pixels (for the matter of the resulting size)
bool TGAImage::unload_rle_data(std::ofstream &out) {
const unsigned char max_chunk_length = 128;
unsigned long npixels = width*height;
unsigned long curpix = 0;
while (curpix<npixels) {
unsigned long chunkstart = curpix*bytespp;
unsigned long curbyte = curpix*bytespp;
unsigned char run_length = 1;
bool raw = true;
while (curpix+run_length<npixels && run_length<max_chunk_length) {
bool succ_eq = true;
for (int t=0; succ_eq && t<bytespp; t++) {
succ_eq = (data[curbyte+t]==data[curbyte+t+bytespp]);
}
curbyte += bytespp;
if (1==run_length) {
raw = !succ_eq;
}
if (raw && succ_eq) {
run_length--;
break;
}
if (!raw && !succ_eq) {
break;
}
run_length++;
}
curpix += run_length;
out.put(raw?run_length-1:run_length+127);
if (!out.good()) {
std::cerr << "can't dump the tga file\n";
return false;
}
out.write((char *)(data+chunkstart), (raw?run_length*bytespp:bytespp));
if (!out.good()) {
std::cerr << "can't dump the tga file\n";
return false;
}
}
return true;
}
TGAColor TGAImage::get(int x, int y) {
if (!data || x<0 || y<0 || x>=width || y>=height) {
return TGAColor();
}
return TGAColor(data+(x+y*width)*bytespp, bytespp);
}
bool TGAImage::set(int x, int y, TGAColor &c) {
if (!data || x<0 || y<0 || x>=width || y>=height) {
return false;
}
memcpy(data+(x+y*width)*bytespp, c.bgra, bytespp);
return true;
}
bool TGAImage::set(int x, int y, const TGAColor &c) {
if (!data || x<0 || y<0 || x>=width || y>=height) {
return false;
}
memcpy(data+(x+y*width)*bytespp, c.bgra, bytespp);
return true;
}
int TGAImage::get_bytespp() {
return bytespp;
}
int TGAImage::get_width() {
return width;
}
int TGAImage::get_height() {
return height;
}
bool TGAImage::flip_horizontally() {
if (!data) return false;
int half = width>>1;
for (int i=0; i<half; i++) {
for (int j=0; j<height; j++) {
TGAColor c1 = get(i, j);
TGAColor c2 = get(width-1-i, j);
set(i, j, c2);
set(width-1-i, j, c1);
}
}
return true;
}
bool TGAImage::flip_vertically() {
if (!data) return false;
unsigned long bytes_per_line = width*bytespp;
unsigned char *line = new unsigned char[bytes_per_line];
int half = height>>1;
for (int j=0; j<half; j++) {
unsigned long l1 = j*bytes_per_line;
unsigned long l2 = (height-1-j)*bytes_per_line;
memmove((void *)line, (void *)(data+l1), bytes_per_line);
memmove((void *)(data+l1), (void *)(data+l2), bytes_per_line);
memmove((void *)(data+l2), (void *)line, bytes_per_line);
}
delete [] line;
return true;
}
unsigned char *TGAImage::buffer() {
return data;
}
void TGAImage::clear() {
memset((void *)data, 0, width*height*bytespp);
}
bool TGAImage::scale(int w, int h) {
if (w<=0 || h<=0 || !data) return false;
unsigned char *tdata = new unsigned char[w*h*bytespp];
int nscanline = 0;
int oscanline = 0;
int erry = 0;
unsigned long nlinebytes = w*bytespp;
unsigned long olinebytes = width*bytespp;
for (int j=0; j<height; j++) {
int errx = width-w;
int nx = -bytespp;
int ox = -bytespp;
for (int i=0; i<width; i++) {
ox += bytespp;
errx += w;
while (errx>=(int)width) {
errx -= width;
nx += bytespp;
memcpy(tdata+nscanline+nx, data+oscanline+ox, bytespp);
}
}
erry += h;
oscanline += olinebytes;
while (erry>=(int)height) {
if (erry>=(int)height<<1) // it means we jump over a scanline
memcpy(tdata+nscanline+nlinebytes, tdata+nscanline, nlinebytes);
erry -= height;
nscanline += nlinebytes;
}
}
delete [] data;
data = tdata;
width = w;
height = h;
return true;
}

View File

@@ -0,0 +1,98 @@
#ifndef __IMAGE_H__
#define __IMAGE_H__
#include <fstream>
#pragma pack(push,1)
struct TGA_Header {
char idlength;
char colormaptype;
char datatypecode;
short colormaporigin;
short colormaplength;
char colormapdepth;
short x_origin;
short y_origin;
short width;
short height;
char bitsperpixel;
char imagedescriptor;
};
#pragma pack(pop)
struct TGAColor {
unsigned char bgra[4];
unsigned char bytespp;
TGAColor() : bgra(), bytespp(1) {
for (int i=0; i<4; i++) bgra[i] = 0;
}
TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) {
bgra[0] = B;
bgra[1] = G;
bgra[2] = R;
bgra[3] = A;
}
TGAColor(unsigned char v) : bgra(), bytespp(1) {
for (int i=0; i<4; i++) bgra[i] = 0;
bgra[0] = v;
}
TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) {
for (int i=0; i<(int)bpp; i++) {
bgra[i] = p[i];
}
for (int i=bpp; i<4; i++) {
bgra[i] = 0;
}
}
unsigned char& operator[](const int i) { return bgra[i]; }
TGAColor operator *(float intensity) const {
TGAColor res = *this;
intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity));
for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity;
return res;
}
};
class TGAImage {
protected:
unsigned char* data;
int width;
int height;
int bytespp;
bool load_rle_data(std::ifstream &in);
bool unload_rle_data(std::ofstream &out);
public:
enum Format {
GRAYSCALE=1, RGB=3, RGBA=4
};
TGAImage();
TGAImage(int w, int h, int bpp);
TGAImage(const TGAImage &img);
bool read_tga_file(const char *filename);
bool write_tga_file(const char *filename, bool rle=true);
bool flip_horizontally();
bool flip_vertically();
bool scale(int w, int h);
TGAColor get(int x, int y);
bool set(int x, int y, TGAColor &c);
bool set(int x, int y, const TGAColor &c);
~TGAImage();
TGAImage & operator =(const TGAImage &img);
int get_width();
int get_height();
int get_bytespp();
unsigned char *buffer();
void clear();
};
#endif //__IMAGE_H__