Merge pull request #607 from erwincoumans/master
add TinyRenderer software renderer
This commit is contained in:
@@ -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
13
data/floor.mtl
Normal 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
18
data/floor.obj
Normal 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
BIN
data/floor_diffuse.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
BIN
data/floor_diffuse.tga
Normal file
BIN
data/floor_diffuse.tga
Normal file
Binary file not shown.
BIN
data/floor_nm_tangent.tga
Normal file
BIN
data/floor_nm_tangent.tga
Normal file
Binary file not shown.
Binary file not shown.
@@ -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++)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
13
examples/TinyRenderer/LICENSE.txt
Normal file
13
examples/TinyRenderer/LICENSE.txt
Normal 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.
|
||||
|
||||
172
examples/TinyRenderer/TinyRenderer.cpp
Normal file
172
examples/TinyRenderer/TinyRenderer.cpp
Normal 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");
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
39
examples/TinyRenderer/TinyRenderer.h
Normal file
39
examples/TinyRenderer/TinyRenderer.h
Normal 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
|
||||
7
examples/TinyRenderer/geometry.cpp
Normal file
7
examples/TinyRenderer/geometry.cpp
Normal 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) {}
|
||||
|
||||
222
examples/TinyRenderer/geometry.h
Normal file
222
examples/TinyRenderer/geometry.h
Normal 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__
|
||||
|
||||
209
examples/TinyRenderer/main.cpp
Normal file
209
examples/TinyRenderer/main.cpp
Normal 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;
|
||||
}
|
||||
108
examples/TinyRenderer/model.cpp
Normal file
108
examples/TinyRenderer/model.cpp
Normal 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();
|
||||
}
|
||||
|
||||
33
examples/TinyRenderer/model.h
Normal file
33
examples/TinyRenderer/model.h
Normal 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__
|
||||
|
||||
93
examples/TinyRenderer/our_gl.cpp
Normal file
93
examples/TinyRenderer/our_gl.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
examples/TinyRenderer/our_gl.h
Normal file
22
examples/TinyRenderer/our_gl.h
Normal 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__
|
||||
|
||||
29
examples/TinyRenderer/premake4.lua
Normal file
29
examples/TinyRenderer/premake4.lua
Normal 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
|
||||
356
examples/TinyRenderer/tgaimage.cpp
Normal file
356
examples/TinyRenderer/tgaimage.cpp
Normal 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;
|
||||
}
|
||||
|
||||
98
examples/TinyRenderer/tgaimage.h
Normal file
98
examples/TinyRenderer/tgaimage.h
Normal 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__
|
||||
|
||||
Reference in New Issue
Block a user