diff --git a/build3/premake4.lua b/build3/premake4.lua index 7f60a1d0e..108f33473 100644 --- a/build3/premake4.lua +++ b/build3/premake4.lua @@ -181,6 +181,7 @@ include "../examples/OpenGLWindow" include "../examples/ThirdPartyLibs/Gwen" include "../examples/SimpleOpenGL3" + include "../examples/TinyRenderer" include "../examples/HelloWorld" include "../examples/BasicDemo" diff --git a/data/floor.mtl b/data/floor.mtl new file mode 100644 index 000000000..8a67048e1 --- /dev/null +++ b/data/floor.mtl @@ -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 diff --git a/data/floor.obj b/data/floor.obj new file mode 100644 index 000000000..42e145867 --- /dev/null +++ b/data/floor.obj @@ -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 + diff --git a/data/floor_diffuse.jpg b/data/floor_diffuse.jpg new file mode 100644 index 000000000..36a26252d Binary files /dev/null and b/data/floor_diffuse.jpg differ diff --git a/data/floor_diffuse.tga b/data/floor_diffuse.tga new file mode 100644 index 000000000..7bb70dd10 Binary files /dev/null and b/data/floor_diffuse.tga differ diff --git a/data/floor_nm_tangent.tga b/data/floor_nm_tangent.tga new file mode 100644 index 000000000..3b8474e8f Binary files /dev/null and b/data/floor_nm_tangent.tga differ diff --git a/data/multibody.bullet b/data/multibody.bullet index 04455f0d5..1074b55dc 100644 Binary files a/data/multibody.bullet and b/data/multibody.bullet differ diff --git a/examples/Importers/ImportObjDemo/ImportObjExample.cpp b/examples/Importers/ImportObjDemo/ImportObjExample.cpp index db6ef97e0..26b0e95e8 100644 --- a/examples/Importers/ImportObjDemo/ImportObjExample.cpp +++ b/examples/Importers/ImportObjDemo/ImportObjExample.cpp @@ -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 && im_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 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); diff --git a/examples/OpenGLWindow/X11OpenGLWindow.cpp b/examples/OpenGLWindow/X11OpenGLWindow.cpp index 681c3f423..e6d0f666d 100644 --- a/examples/OpenGLWindow/X11OpenGLWindow.cpp +++ b/examples/OpenGLWindow/X11OpenGLWindow.cpp @@ -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; - + } diff --git a/examples/SimpleOpenGL3/main.cpp b/examples/SimpleOpenGL3/main.cpp index 604eb3920..015db129e 100644 --- a/examples/SimpleOpenGL3/main.cpp +++ b/examples/SimpleOpenGL3/main.cpp @@ -1,5 +1,5 @@ #include "OpenGLWindow/SimpleOpenGL3App.h" -#include "Bullet3Common/b3Vector3.h" +#include "Bullet3Common/b3Quaternion.h" #include "Bullet3Common/b3CommandLineArgs.h" #include "assert.h" #include @@ -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); diff --git a/examples/TinyRenderer/LICENSE.txt b/examples/TinyRenderer/LICENSE.txt new file mode 100644 index 000000000..95dbe5b4e --- /dev/null +++ b/examples/TinyRenderer/LICENSE.txt @@ -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. + diff --git a/examples/TinyRenderer/TinyRenderer.cpp b/examples/TinyRenderer/TinyRenderer.cpp new file mode 100644 index 000000000..bbc9bbf16 --- /dev/null +++ b/examples/TinyRenderer/TinyRenderer.cpp @@ -0,0 +1,172 @@ +#include "TinyRenderer.h" + +#include +#include +#include +#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 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& zbuffer = renderData.m_depthBuffer; + + //todo(erwincoumans) make this a separate call + for (int i=width*height; i--; zbuffer[i] = -std::numeric_limits::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; mnfaces(); 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"); + + +} + + diff --git a/examples/TinyRenderer/TinyRenderer.h b/examples/TinyRenderer/TinyRenderer.h new file mode 100644 index 000000000..1bcd49b95 --- /dev/null +++ b/examples/TinyRenderer/TinyRenderer.h @@ -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 m_depthBuffer; + + TinyRenderObjectData(int width, int height, const char* objFileName); + virtual ~TinyRenderObjectData(); + +}; + + +class TinyRenderer +{ + public: + static void renderObject(TinyRenderObjectData& renderData); +}; + +#endif // TINY_RENDERER_Hbla diff --git a/examples/TinyRenderer/geometry.cpp b/examples/TinyRenderer/geometry.cpp new file mode 100644 index 000000000..3b6b2f251 --- /dev/null +++ b/examples/TinyRenderer/geometry.cpp @@ -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) {} + diff --git a/examples/TinyRenderer/geometry.h b/examples/TinyRenderer/geometry.h new file mode 100644 index 000000000..38bb4d935 --- /dev/null +++ b/examples/TinyRenderer/geometry.h @@ -0,0 +1,222 @@ +#ifndef __GEOMETRY_H__ +#define __GEOMETRY_H__ +#include +#include +#include + + +template class mat; + +template struct vec { + vec() { for (size_t i=DIM; i--; data_[i] = T()); } + T& operator[](const size_t i) { assert(i struct vec<2,T> { + vec() : x(T()), y(T()) {} + vec(T X, T Y) : x(X), y(Y) {} + template 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 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 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 T operator*(const vec& lhs, const vec& rhs) { + T ret = T(); + for (size_t i=DIM; i--; ret+=lhs[i]*rhs[i]); + return ret; +} + + +templatevec operator+(vec lhs, const vec& rhs) { + for (size_t i=DIM; i--; lhs[i]+=rhs[i]); + return lhs; +} + +templatevec operator-(vec lhs, const vec& rhs) { + for (size_t i=DIM; i--; lhs[i]-=rhs[i]); + return lhs; +} + +template vec operator*(vec lhs, const U& rhs) { + for (size_t i=DIM; i--; lhs[i]*=rhs); + return lhs; +} + +template vec operator/(vec lhs, const U& rhs) { + for (size_t i=DIM; i--; lhs[i]/=rhs); + return lhs; +} + +template vec embed(const vec &v, T fill=1) { + vec ret; + for (size_t i=LEN; i--; ret[i]=(i vec proj(const vec &v) { + vec ret; + for (size_t i=LEN; i--; ret[i]=v[i]); + return ret; +} + +template 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 std::ostream& operator<<(std::ostream& out, vec& v) { + for(unsigned int i=0; i struct dt { + static T det(const mat& src) { + T ret=0; + for (size_t i=DIM; i--; ret += src[0][i]*src.cofactor(0,i)); + return ret; + } +}; + +template struct dt<1,T> { + static T det(const mat<1,1,T>& src) { + return src[0][0]; + } +}; + +///////////////////////////////////////////////////////////////////////////////// + +template class mat { + vec rows[DimRows]; +public: + mat() {} + + vec& operator[] (const size_t idx) { + assert(idx& operator[] (const size_t idx) const { + assert(idx col(const size_t idx) const { + assert(idx ret; + for (size_t i=DimRows; i--; ret[i]=rows[i][idx]); + return ret; + } + + void set_col(size_t idx, vec v) { + assert(idx identity() { + mat 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::det(*this); + } + + mat get_minor(size_t row, size_t col) const { + mat ret; + for (size_t i=DimRows-1; i--; ) + for (size_t j=DimCols-1;j--; ret[i][j]=rows[i adjugate() const { + mat ret; + for (size_t i=DimRows; i--; ) + for (size_t j=DimCols; j--; ret[i][j]=cofactor(i,j)); + return ret; + } + + mat invert_transpose() { + mat ret = adjugate(); + T tmp = ret[0]*rows[0]; + return ret/tmp; + } + + mat invert() { + return invert_transpose().transpose(); + } + + mat transpose() { + mat ret; + for (size_t i=DimCols; i--; ret[i]=this->col(i)); + return ret; + } +}; + +///////////////////////////////////////////////////////////////////////////////// + +template vec operator*(const mat& lhs, const vec& rhs) { + vec ret; + for (size_t i=DimRows; i--; ret[i]=lhs[i]*rhs); + return ret; +} + +templatemat operator*(const mat& lhs, const mat& rhs) { + mat result; + for (size_t i=R1; i--; ) + for (size_t j=C2; j--; result[i][j]=lhs[i]*rhs.col(j)); + return result; +} + +templatemat operator/(mat lhs, const T& rhs) { + for (size_t i=DimRows; i--; lhs[i]=lhs[i]/rhs); + return lhs; +} + +#if 0 +template std::ostream& operator<<(std::ostream& out, mat& m) { + for (size_t i=0; i 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__ + diff --git a/examples/TinyRenderer/main.cpp b/examples/TinyRenderer/main.cpp new file mode 100644 index 000000000..65a3352f6 --- /dev/null +++ b/examples/TinyRenderer/main.cpp @@ -0,0 +1,209 @@ +#include "OpenGLWindow/SimpleOpenGL3App.h" +#include "Bullet3Common/b3Quaternion.h" +#include "Bullet3Common/b3CommandLineArgs.h" +#include "assert.h" +#include + +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>4; + unsigned char* pi=image+y*textureWidth*3; + for(int x=0;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; +} diff --git a/examples/TinyRenderer/model.cpp b/examples/TinyRenderer/model.cpp new file mode 100644 index 000000000..bc7983dd5 --- /dev/null +++ b/examples/TinyRenderer/model.cpp @@ -0,0 +1,108 @@ +#include +#include +#include +#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 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 Model::face(int idx) { + std::vector 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(); +} + diff --git a/examples/TinyRenderer/model.h b/examples/TinyRenderer/model.h new file mode 100644 index 000000000..035cc119a --- /dev/null +++ b/examples/TinyRenderer/model.h @@ -0,0 +1,33 @@ +#ifndef __MODEL_H__ +#define __MODEL_H__ +#include +#include +#include "geometry.h" +#include "tgaimage.h" + +class Model { +private: + std::vector verts_; + std::vector > faces_; // attention, this Vec3i means vertex/uv/normal + std::vector norms_; + std::vector 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 face(int idx); +}; +#endif //__MODEL_H__ + diff --git a/examples/TinyRenderer/our_gl.cpp b/examples/TinyRenderer/our_gl.cpp new file mode 100644 index 000000000..4c99c0685 --- /dev/null +++ b/examples/TinyRenderer/our_gl.cpp @@ -0,0 +1,93 @@ +#include +#include +#include +#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::max(), std::numeric_limits::max()); + Vec2f bboxmax(-std::numeric_limits::max(), -std::numeric_limits::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); + } + } + } +} + diff --git a/examples/TinyRenderer/our_gl.h b/examples/TinyRenderer/our_gl.h new file mode 100644 index 000000000..d80156904 --- /dev/null +++ b/examples/TinyRenderer/our_gl.h @@ -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__ + diff --git a/examples/TinyRenderer/premake4.lua b/examples/TinyRenderer/premake4.lua new file mode 100644 index 000000000..3eb417ff2 --- /dev/null +++ b/examples/TinyRenderer/premake4.lua @@ -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 diff --git a/examples/TinyRenderer/tgaimage.cpp b/examples/TinyRenderer/tgaimage.cpp new file mode 100644 index 000000000..47e6ff833 --- /dev/null +++ b/examples/TinyRenderer/tgaimage.cpp @@ -0,0 +1,356 @@ +#include +#include +#include +#include +#include +#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; ipixelcount) { + 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; ipixelcount) { + 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=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>1; + for (int j=0; j=(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; +} + diff --git a/examples/TinyRenderer/tgaimage.h b/examples/TinyRenderer/tgaimage.h new file mode 100644 index 000000000..63a944bca --- /dev/null +++ b/examples/TinyRenderer/tgaimage.h @@ -0,0 +1,98 @@ +#ifndef __IMAGE_H__ +#define __IMAGE_H__ + +#include + +#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__ +