* Adjustment: Update Bullet version to 3.24.

This commit is contained in:
Robert MacGregor 2022-06-27 10:01:08 -04:00
parent 35de012ee7
commit 4a3f31df2a
6148 changed files with 2112532 additions and 56873 deletions

View file

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

View file

@ -0,0 +1,656 @@
#include "TinyRenderer.h"
#include <cmath>
#include <iostream>
#include <limits>
#include <vector>
#include "../CommonInterfaces/CommonFileIOInterface.h"
#include "../OpenGLWindow/ShapeData.h"
#include "../Utils/b3BulletDefaultFileIO.h"
#include "../Utils/b3ResourcePath.h"
#include "Bullet3Common/b3Logging.h"
#include "Bullet3Common/b3MinMax.h"
#include "LinearMath/btAlignedObjectArray.h"
#include "LinearMath/btVector3.h"
#include "geometry.h"
#include "model.h"
#include "our_gl.h"
#include "tgaimage.h"
using namespace TinyRender;
struct DepthShader : public IShader
{
Model* m_model;
Matrix& m_modelMat;
Matrix m_invModelMat;
Matrix& m_projectionMat;
Vec3f m_localScaling;
Matrix& m_lightModelView;
float m_lightDistance;
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
DepthShader(Model* model, Matrix& lightModelView, Matrix& projectionMat, Matrix& modelMat, Vec3f localScaling, float lightDistance)
: m_model(model),
m_modelMat(modelMat),
m_projectionMat(projectionMat),
m_localScaling(localScaling),
m_lightModelView(lightModelView),
m_lightDistance(lightDistance)
{
m_nearPlane = m_projectionMat.col(3)[2] / (m_projectionMat.col(2)[2] - 1);
m_farPlane = m_projectionMat.col(3)[2] / (m_projectionMat.col(2)[2] + 1);
m_invModelMat = m_modelMat.invert_transpose();
}
virtual Vec4f vertex(int iface, int nthvert)
{
Vec2f uv = m_model->uv(iface, nthvert);
varying_uv.set_col(nthvert, uv);
varying_nrm.set_col(nthvert, proj<3>(m_invModelMat * embed<4>(m_model->normal(iface, nthvert), 0.f)));
Vec3f unScaledVert = m_model->vert(iface, nthvert);
Vec3f scaledVert = Vec3f(unScaledVert[0] * m_localScaling[0],
unScaledVert[1] * m_localScaling[1],
unScaledVert[2] * m_localScaling[2]);
Vec4f gl_Vertex = m_projectionMat * m_lightModelView * embed<4>(scaledVert);
varying_tri.set_col(nthvert, gl_Vertex);
return gl_Vertex;
}
virtual bool fragment(Vec3f bar, TGAColor& color)
{
Vec4f p = varying_tri * bar;
color = TGAColor(255, 255, 255) * (p[2] / m_lightDistance);
return false;
}
};
struct Shader : public IShader
{
Model* m_model;
Vec3f m_light_dir_local;
Vec3f m_light_color;
Matrix& m_modelMat;
Matrix m_invModelMat;
Matrix& m_modelView1;
Matrix& m_projectionMat;
Vec3f m_localScaling;
Matrix& m_lightModelView;
Vec4f m_colorRGBA;
Matrix& m_viewportMat;
Matrix m_projectionModelViewMat;
Matrix m_projectionLightViewMat;
float m_ambient_coefficient;
float m_diffuse_coefficient;
float m_specular_coefficient;
b3AlignedObjectArray<float>* m_shadowBuffer;
int m_width;
int m_height;
int m_index;
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<4, 3, float> varying_tri_light_view;
mat<3, 3, float> varying_nrm; // normal per vertex to be interpolated by FS
mat<4, 3, float> world_tri; // model triangle coordinates in the world space used for backface culling, written by VS
Shader(Model* model, Vec3f light_dir_local, Vec3f light_color, Matrix& modelView, Matrix& lightModelView, Matrix& projectionMat, Matrix& modelMat, Matrix& viewportMat, Vec3f localScaling, const Vec4f& colorRGBA, int width, int height, b3AlignedObjectArray<float>* shadowBuffer, float ambient_coefficient = 0.6, float diffuse_coefficient = 0.35, float specular_coefficient = 0.05)
: m_model(model),
m_light_dir_local(light_dir_local),
m_light_color(light_color),
m_modelMat(modelMat),
m_modelView1(modelView),
m_projectionMat(projectionMat),
m_localScaling(localScaling),
m_lightModelView(lightModelView),
m_colorRGBA(colorRGBA),
m_viewportMat(viewportMat),
m_ambient_coefficient(ambient_coefficient),
m_diffuse_coefficient(diffuse_coefficient),
m_specular_coefficient(specular_coefficient),
m_shadowBuffer(shadowBuffer),
m_width(width),
m_height(height)
{
m_nearPlane = m_projectionMat.col(3)[2] / (m_projectionMat.col(2)[2] - 1);
m_farPlane = m_projectionMat.col(3)[2] / (m_projectionMat.col(2)[2] + 1);
//printf("near=%f, far=%f\n", m_nearPlane, m_farPlane);
m_invModelMat = m_modelMat.invert_transpose();
m_projectionModelViewMat = m_projectionMat * m_modelView1;
m_projectionLightViewMat = m_projectionMat * m_lightModelView;
}
virtual Vec4f vertex(int iface, int nthvert)
{
//B3_PROFILE("vertex");
Vec2f uv = m_model->uv(iface, nthvert);
varying_uv.set_col(nthvert, uv);
varying_nrm.set_col(nthvert, proj<3>(m_invModelMat * embed<4>(m_model->normal(iface, nthvert), 0.f)));
Vec3f unScaledVert = m_model->vert(iface, nthvert);
Vec3f scaledVert = Vec3f(unScaledVert[0] * m_localScaling[0],
unScaledVert[1] * m_localScaling[1],
unScaledVert[2] * m_localScaling[2]);
Vec4f gl_Vertex = m_projectionModelViewMat * embed<4>(scaledVert);
varying_tri.set_col(nthvert, gl_Vertex);
Vec4f world_Vertex = m_modelMat * embed<4>(scaledVert);
world_tri.set_col(nthvert, world_Vertex);
Vec4f gl_VertexLightView = m_projectionLightViewMat * embed<4>(scaledVert);
varying_tri_light_view.set_col(nthvert, gl_VertexLightView);
return gl_Vertex;
}
virtual bool fragment(Vec3f bar, TGAColor& color)
{
//B3_PROFILE("fragment");
Vec4f p = m_viewportMat * (varying_tri_light_view * bar);
float depth = p[2];
p = p / p[3];
float index_x = b3Max(float(0.0), b3Min(float(m_width - 1), p[0]));
float index_y = b3Max(float(0.0), b3Min(float(m_height - 1), p[1]));
int idx = int(index_x) + int(index_y) * m_width; // index in the shadowbuffer array
float shadow = 1.0;
if (m_shadowBuffer && idx >=0 && idx <m_shadowBuffer->size())
{
shadow = 0.8 + 0.2 * (m_shadowBuffer->at(idx) < -depth + 0.05); // magic coeff to avoid z-fighting
}
Vec3f bn = (varying_nrm * bar).normalize();
Vec2f uv = varying_uv * bar;
Vec3f reflection_direction = (bn * (bn * m_light_dir_local * 2.f) - m_light_dir_local).normalize();
float specular = std::pow(b3Max(reflection_direction.z, 0.f),
m_model->specular(uv));
float diffuse = b3Max(0.f, bn * m_light_dir_local);
color = m_model->diffuse(uv);
color[0] *= m_colorRGBA[0];
color[1] *= m_colorRGBA[1];
color[2] *= m_colorRGBA[2];
color[3] *= m_colorRGBA[3];
for (int i = 0; i < 3; ++i)
{
int orgColor = 0;
float floatColor = (m_ambient_coefficient * color[i] + shadow * (m_diffuse_coefficient * diffuse + m_specular_coefficient * specular) * color[i] * m_light_color[i]);
if (floatColor==floatColor)
{
orgColor=int(floatColor);
}
color[i] = b3Min(orgColor, 255);
}
return false;
}
};
TinyRenderObjectData::TinyRenderObjectData(TGAImage& rgbColorBuffer, b3AlignedObjectArray<float>& depthBuffer, b3AlignedObjectArray<float>* shadowBuffer)
: m_model(0),
m_rgbColorBuffer(rgbColorBuffer),
m_depthBuffer(depthBuffer),
m_shadowBuffer(shadowBuffer),
m_segmentationMaskBufferPtr(0),
m_userData(0),
m_userIndex(-1),
m_objectIndex(-1),
m_doubleSided(false)
{
Vec3f eye(1, 1, 3);
Vec3f center(0, 0, 0);
Vec3f up(0, 0, 1);
m_lightDirWorld.setValue(0, 0, 0);
m_lightColor.setValue(1, 1, 1);
m_localScaling.setValue(1, 1, 1);
m_modelMatrix = Matrix::identity();
m_lightAmbientCoeff = 0.6;
m_lightDiffuseCoeff = 0.35;
m_lightSpecularCoeff = 0.05;
}
TinyRenderObjectData::TinyRenderObjectData(TGAImage& rgbColorBuffer, b3AlignedObjectArray<float>& depthBuffer, b3AlignedObjectArray<float>* shadowBuffer, b3AlignedObjectArray<int>* segmentationMaskBuffer, int objectIndex, int linkIndex)
: m_model(0),
m_rgbColorBuffer(rgbColorBuffer),
m_depthBuffer(depthBuffer),
m_shadowBuffer(shadowBuffer),
m_segmentationMaskBufferPtr(segmentationMaskBuffer),
m_userData(0),
m_userIndex(-1),
m_objectIndex(objectIndex),
m_linkIndex(linkIndex),
m_doubleSided(false)
{
Vec3f eye(1, 1, 3);
Vec3f center(0, 0, 0);
Vec3f up(0, 0, 1);
m_lightDirWorld.setValue(0, 0, 0);
m_lightColor.setValue(1, 1, 1);
m_localScaling.setValue(1, 1, 1);
m_modelMatrix = Matrix::identity();
m_lightAmbientCoeff = 0.6;
m_lightDiffuseCoeff = 0.35;
m_lightSpecularCoeff = 0.05;
}
TinyRenderObjectData::TinyRenderObjectData(TGAImage& rgbColorBuffer, b3AlignedObjectArray<float>& depthBuffer)
: m_model(0),
m_rgbColorBuffer(rgbColorBuffer),
m_depthBuffer(depthBuffer),
m_shadowBuffer(0),
m_segmentationMaskBufferPtr(0),
m_userData(0),
m_userIndex(-1),
m_objectIndex(-1),
m_doubleSided(false)
{
Vec3f eye(1, 1, 3);
Vec3f center(0, 0, 0);
Vec3f up(0, 0, 1);
m_lightDirWorld.setValue(0, 0, 0);
m_lightDistance = 10;
m_lightColor.setValue(1, 1, 1);
m_localScaling.setValue(1, 1, 1);
m_modelMatrix = Matrix::identity();
m_lightAmbientCoeff = 0.6;
m_lightDiffuseCoeff = 0.35;
m_lightSpecularCoeff = 0.05;
}
TinyRenderObjectData::TinyRenderObjectData(TGAImage& rgbColorBuffer, b3AlignedObjectArray<float>& depthBuffer, b3AlignedObjectArray<int>* segmentationMaskBuffer, int objectIndex)
: m_model(0),
m_rgbColorBuffer(rgbColorBuffer),
m_depthBuffer(depthBuffer),
m_shadowBuffer(0),
m_segmentationMaskBufferPtr(segmentationMaskBuffer),
m_userData(0),
m_userIndex(-1),
m_objectIndex(objectIndex),
m_doubleSided(false)
{
Vec3f eye(1, 1, 3);
Vec3f center(0, 0, 0);
Vec3f up(0, 0, 1);
m_lightDirWorld.setValue(0, 0, 0);
m_lightColor.setValue(1, 1, 1);
m_localScaling.setValue(1, 1, 1);
m_modelMatrix = Matrix::identity();
m_lightAmbientCoeff = 0.6;
m_lightDiffuseCoeff = 0.35;
m_lightSpecularCoeff = 0.05;
}
void TinyRenderObjectData::loadModel(const char* fileName, CommonFileIOInterface* fileIO)
{
//todo(erwincoumans) move the file loading out of here
char relativeFileName[1024];
if (!fileIO->findResourcePath(fileName, relativeFileName, 1024))
{
printf("Cannot find file %s\n", fileName);
}
else
{
m_model = new Model(relativeFileName);
}
}
void TinyRenderObjectData::registerMeshShape(const float* vertices, int numVertices, const int* indices, int numIndices, const float rgbaColor[4],
unsigned char* textureImage, int textureWidth, int textureHeight)
{
if (0 == m_model)
{
{
B3_PROFILE("setColorRGBA");
m_model = new Model();
m_model->setColorRGBA(rgbaColor);
}
if (textureImage)
{
{
B3_PROFILE("setDiffuseTextureFromData");
m_model->setDiffuseTextureFromData(textureImage, textureWidth, textureHeight);
}
}
else
{
/*char relativeFileName[1024];
if (b3ResourcePath::findResourcePath("floor_diffuse.tga", relativeFileName, 1024))
{
m_model->loadDiffuseTexture(relativeFileName);
}
*/
}
{
B3_PROFILE("reserveMemory");
m_model->reserveMemory(numVertices, numIndices);
}
{
B3_PROFILE("addVertex");
for (int i = 0; i < numVertices; i++)
{
m_model->addVertex(vertices[i * 9],
vertices[i * 9 + 1],
vertices[i * 9 + 2],
vertices[i * 9 + 4],
vertices[i * 9 + 5],
vertices[i * 9 + 6],
vertices[i * 9 + 7],
vertices[i * 9 + 8]);
}
}
{
B3_PROFILE("addTriangle");
for (int i = 0; i < numIndices; i += 3)
{
m_model->addTriangle(indices[i], indices[i], indices[i],
indices[i + 1], indices[i + 1], indices[i + 1],
indices[i + 2], indices[i + 2], indices[i + 2]);
}
}
}
}
void TinyRenderObjectData::registerMesh2(btAlignedObjectArray<btVector3>& vertices, btAlignedObjectArray<btVector3>& normals, btAlignedObjectArray<int>& indices, CommonFileIOInterface* fileIO)
{
if (0 == m_model)
{
int numVertices = vertices.size();
int numIndices = indices.size();
m_model = new Model();
char relativeFileName[1024];
if (fileIO->findResourcePath("floor_diffuse.tga", relativeFileName, 1024))
{
m_model->loadDiffuseTexture(relativeFileName);
}
for (int i = 0; i < numVertices; i++)
{
m_model->addVertex(vertices[i].x(),
vertices[i].y(),
vertices[i].z(),
normals[i].x(),
normals[i].y(),
normals[i].z(),
0.5, 0.5);
}
for (int i = 0; i < numIndices; i += 3)
{
m_model->addTriangle(indices[i], indices[i], indices[i],
indices[i + 1], indices[i + 1], indices[i + 1],
indices[i + 2], indices[i + 2], indices[i + 2]);
}
}
}
void TinyRenderObjectData::createCube(float halfExtentsX, float halfExtentsY, float halfExtentsZ, CommonFileIOInterface* fileIO)
{
b3BulletDefaultFileIO defaultFileIO;
if (fileIO==0)
{
fileIO = &defaultFileIO;
}
m_model = new Model();
char relativeFileName[1024];
if (fileIO->findResourcePath("floor_diffuse.tga", relativeFileName, 1024))
{
m_model->loadDiffuseTexture(relativeFileName);
}
int strideInBytes = 9 * sizeof(float);
int numVertices = sizeof(cube_vertices_textured) / strideInBytes;
int numIndices = sizeof(cube_indices) / sizeof(int);
for (int i = 0; i < numVertices; i++)
{
m_model->addVertex(halfExtentsX * cube_vertices_textured[i * 9],
halfExtentsY * cube_vertices_textured[i * 9 + 1],
halfExtentsZ * cube_vertices_textured[i * 9 + 2],
cube_vertices_textured[i * 9 + 4],
cube_vertices_textured[i * 9 + 5],
cube_vertices_textured[i * 9 + 6],
cube_vertices_textured[i * 9 + 7],
cube_vertices_textured[i * 9 + 8]);
}
for (int i = 0; i < numIndices; i += 3)
{
m_model->addTriangle(cube_indices[i], cube_indices[i], cube_indices[i],
cube_indices[i + 1], cube_indices[i + 1], cube_indices[i + 1],
cube_indices[i + 2], cube_indices[i + 2], cube_indices[i + 2]);
}
}
TinyRenderObjectData::~TinyRenderObjectData()
{
delete m_model;
}
static bool equals(const Vec4f& vA, const Vec4f& vB)
{
return false;
}
static void clipEdge(const mat<4, 3, float>& triangleIn, int vertexIndexA, int vertexIndexB, b3AlignedObjectArray<Vec4f>& vertices)
{
Vec4f v0New = triangleIn.col(vertexIndexA);
Vec4f v1New = triangleIn.col(vertexIndexB);
bool v0Inside = v0New[3] > 0.f && v0New[2] > -v0New[3];
bool v1Inside = v1New[3] > 0.f && v1New[2] > -v1New[3];
if (v0Inside && v1Inside)
{
}
else if (v0Inside || v1Inside)
{
float d0 = v0New[2] + v0New[3];
float d1 = v1New[2] + v1New[3];
float factor = 1.0 / (d1 - d0);
Vec4f newVertex = (v0New * d1 - v1New * d0) * factor;
if (v0Inside)
{
v1New = newVertex;
}
else
{
v0New = newVertex;
}
}
else
{
return;
}
if (vertices.size() == 0 || !(equals(vertices[vertices.size() - 1], v0New)))
{
vertices.push_back(v0New);
}
vertices.push_back(v1New);
}
static bool clipTriangleAgainstNearplane(const mat<4, 3, float>& triangleIn, b3AlignedObjectArray<mat<4, 3, float> >& clippedTrianglesOut)
{
//discard triangle if all vertices are behind near-plane
if (triangleIn[3][0] < 0 && triangleIn[3][1] < 0 && triangleIn[3][2] < 0)
{
return true;
}
//accept triangle if all vertices are in front of the near-plane
if (triangleIn[3][0] >= 0 && triangleIn[3][1] >= 0 && triangleIn[3][2] >= 0)
{
clippedTrianglesOut.push_back(triangleIn);
return false;
}
Vec4f vtxCache[5];
b3AlignedObjectArray<Vec4f> vertices;
vertices.initializeFromBuffer(vtxCache, 0, 5);
clipEdge(triangleIn, 0, 1, vertices);
clipEdge(triangleIn, 1, 2, vertices);
clipEdge(triangleIn, 2, 0, vertices);
if (vertices.size() < 3)
return true;
if (equals(vertices[0], vertices[vertices.size() - 1]))
{
vertices.pop_back();
}
//create a fan of triangles
for (int i = 1; i < vertices.size() - 1; i++)
{
mat<4, 3, float>& vtx = clippedTrianglesOut.expand();
vtx.set_col(0, vertices[0]);
vtx.set_col(1, vertices[i]);
vtx.set_col(2, vertices[i + 1]);
}
return true;
}
void TinyRenderer::renderObject(TinyRenderObjectData& renderData)
{
B3_PROFILE("renderObject");
int width = renderData.m_rgbColorBuffer.get_width();
int height = renderData.m_rgbColorBuffer.get_height();
Vec3f light_dir_local = Vec3f(renderData.m_lightDirWorld[0], renderData.m_lightDirWorld[1], renderData.m_lightDirWorld[2]);
Vec3f light_color = Vec3f(renderData.m_lightColor[0], renderData.m_lightColor[1], renderData.m_lightColor[2]);
float light_distance = renderData.m_lightDistance;
Model* model = renderData.m_model;
if (0 == model)
return;
//discard invisible objects (zero alpha)
if (model->getColorRGBA()[3] == 0)
return;
renderData.m_viewportMatrix = viewport(0, 0, width, height);
b3AlignedObjectArray<float>& zbuffer = renderData.m_depthBuffer;
b3AlignedObjectArray<float>* shadowBufferPtr = renderData.m_shadowBuffer;
int* segmentationMaskBufferPtr = (renderData.m_segmentationMaskBufferPtr && renderData.m_segmentationMaskBufferPtr->size()) ? &renderData.m_segmentationMaskBufferPtr->at(0) : 0;
TGAImage& frame = renderData.m_rgbColorBuffer;
{
// light target is set to be the origin, and the up direction is set to be vertical up.
Matrix lightViewMatrix = lookat(light_dir_local * light_distance, Vec3f(0.0, 0.0, 0.0), Vec3f(0.0, 0.0, 1.0));
Matrix lightModelViewMatrix = lightViewMatrix * renderData.m_modelMatrix;
Matrix modelViewMatrix = renderData.m_viewMatrix * renderData.m_modelMatrix;
Vec3f localScaling(renderData.m_localScaling[0], renderData.m_localScaling[1], renderData.m_localScaling[2]);
Matrix viewMatrixInv = renderData.m_viewMatrix.invert();
btVector3 P(viewMatrixInv[0][3], viewMatrixInv[1][3], viewMatrixInv[2][3]);
Shader shader(model, light_dir_local, light_color, modelViewMatrix, lightModelViewMatrix, renderData.m_projectionMatrix, renderData.m_modelMatrix, renderData.m_viewportMatrix, localScaling, model->getColorRGBA(), width, height, shadowBufferPtr, renderData.m_lightAmbientCoeff, renderData.m_lightDiffuseCoeff, renderData.m_lightSpecularCoeff);
{
B3_PROFILE("face");
for (int i = 0; i < model->nfaces(); i++)
{
for (int j = 0; j < 3; j++)
{
shader.vertex(i, j);
}
if (!renderData.m_doubleSided)
{
// backface culling
btVector3 v0(shader.world_tri.col(0)[0], shader.world_tri.col(0)[1], shader.world_tri.col(0)[2]);
btVector3 v1(shader.world_tri.col(1)[0], shader.world_tri.col(1)[1], shader.world_tri.col(1)[2]);
btVector3 v2(shader.world_tri.col(2)[0], shader.world_tri.col(2)[1], shader.world_tri.col(2)[2]);
btVector3 N = (v1 - v0).cross(v2 - v0);
if ((v0 - P).dot(N) >= 0)
continue;
}
mat<4, 3, float> stackTris[3];
b3AlignedObjectArray<mat<4, 3, float> > clippedTriangles;
clippedTriangles.initializeFromBuffer(stackTris, 0, 3);
bool hasClipped = clipTriangleAgainstNearplane(shader.varying_tri, clippedTriangles);
if (hasClipped)
{
for (int t = 0; t < clippedTriangles.size(); t++)
{
triangleClipped(clippedTriangles[t], shader.varying_tri, shader, frame, &zbuffer[0], segmentationMaskBufferPtr, renderData.m_viewportMatrix, renderData.m_objectIndex + ((renderData.m_linkIndex + 1) << 24));
}
}
else
{
triangle(shader.varying_tri, shader, frame, &zbuffer[0], segmentationMaskBufferPtr, renderData.m_viewportMatrix, renderData.m_objectIndex + ((renderData.m_linkIndex + 1) << 24));
}
}
}
}
}
void TinyRenderer::renderObjectDepth(TinyRenderObjectData& renderData)
{
int width = renderData.m_rgbColorBuffer.get_width();
int height = renderData.m_rgbColorBuffer.get_height();
Vec3f light_dir_local = Vec3f(renderData.m_lightDirWorld[0], renderData.m_lightDirWorld[1], renderData.m_lightDirWorld[2]);
float light_distance = renderData.m_lightDistance;
Model* model = renderData.m_model;
if (0 == model)
return;
renderData.m_viewportMatrix = viewport(0, 0, width, height);
float* shadowBufferPtr = (renderData.m_shadowBuffer && renderData.m_shadowBuffer->size()) ? &renderData.m_shadowBuffer->at(0) : 0;
int* segmentationMaskBufferPtr = 0;
TGAImage depthFrame(width, height, TGAImage::RGB);
{
// light target is set to be the origin, and the up direction is set to be vertical up.
Matrix lightViewMatrix = lookat(light_dir_local * light_distance, Vec3f(0.0, 0.0, 0.0), Vec3f(0.0, 0.0, 1.0));
Matrix lightModelViewMatrix = lightViewMatrix * renderData.m_modelMatrix;
Matrix lightViewProjectionMatrix = renderData.m_projectionMatrix;
Vec3f localScaling(renderData.m_localScaling[0], renderData.m_localScaling[1], renderData.m_localScaling[2]);
DepthShader shader(model, lightModelViewMatrix, lightViewProjectionMatrix, renderData.m_modelMatrix, localScaling, light_distance);
for (int i = 0; i < model->nfaces(); i++)
{
for (int j = 0; j < 3; j++)
{
shader.vertex(i, j);
}
mat<4, 3, float> stackTris[3];
b3AlignedObjectArray<mat<4, 3, float> > clippedTriangles;
clippedTriangles.initializeFromBuffer(stackTris, 0, 3);
bool hasClipped = clipTriangleAgainstNearplane(shader.varying_tri, clippedTriangles);
if (hasClipped)
{
for (int t = 0; t < clippedTriangles.size(); t++)
{
triangleClipped(clippedTriangles[t], shader.varying_tri, shader, depthFrame, shadowBufferPtr, segmentationMaskBufferPtr, renderData.m_viewportMatrix, renderData.m_objectIndex);
}
}
else
{
triangle(shader.varying_tri, shader, depthFrame, shadowBufferPtr, segmentationMaskBufferPtr, renderData.m_viewportMatrix, renderData.m_objectIndex);
}
}
}
}

View file

@ -0,0 +1,66 @@
#ifndef TINY_RENDERER_H
#define TINY_RENDERER_H
#include "geometry.h"
#include "model.h"
#include "Bullet3Common/b3AlignedObjectArray.h"
#include "Bullet3Common/b3Vector3.h"
#include "LinearMath/btAlignedObjectArray.h"
#include "LinearMath/btVector3.h"
#include "tgaimage.h"
struct TinyRenderObjectData
{
//Camera
TinyRender::Matrix m_viewMatrix;
TinyRender::Matrix m_projectionMatrix;
TinyRender::Matrix m_viewportMatrix;
btVector3 m_localScaling;
btVector3 m_lightDirWorld;
btVector3 m_lightColor;
float m_lightDistance;
float m_lightAmbientCoeff;
float m_lightDiffuseCoeff;
float m_lightSpecularCoeff;
//Model (vertices, indices, textures, shader)
TinyRender::Matrix m_modelMatrix;
TinyRender::Model* m_model;
//class IShader* m_shader; todo(erwincoumans) expose the shader, for now we use a default shader
//Output
TGAImage& m_rgbColorBuffer;
b3AlignedObjectArray<float>& m_depthBuffer; //required, hence a reference
b3AlignedObjectArray<float>* m_shadowBuffer; //optional, hence a pointer
b3AlignedObjectArray<int>* m_segmentationMaskBufferPtr; //optional, hence a pointer
TinyRenderObjectData(TGAImage& rgbColorBuffer, b3AlignedObjectArray<float>& depthBuffer);
TinyRenderObjectData(TGAImage& rgbColorBuffer, b3AlignedObjectArray<float>& depthBuffer, b3AlignedObjectArray<int>* segmentationMaskBuffer, int objectIndex);
TinyRenderObjectData(TGAImage& rgbColorBuffer, b3AlignedObjectArray<float>& depthBuffer, b3AlignedObjectArray<float>* shadowBuffer);
TinyRenderObjectData(TGAImage& rgbColorBuffer, b3AlignedObjectArray<float>& depthBuffer, b3AlignedObjectArray<float>* shadowBuffer, b3AlignedObjectArray<int>* segmentationMaskBuffer, int objectIndex, int linkIndex);
virtual ~TinyRenderObjectData();
void loadModel(const char* fileName, struct CommonFileIOInterface* fileIO);
void createCube(float HalfExtentsX, float HalfExtentsY, float HalfExtentsZ, struct CommonFileIOInterface* fileIO=0);
void registerMeshShape(const float* vertices, int numVertices, const int* indices, int numIndices, const float rgbaColor[4],
unsigned char* textureImage = 0, int textureWidth = 0, int textureHeight = 0);
void registerMesh2(btAlignedObjectArray<btVector3>& vertices, btAlignedObjectArray<btVector3>& normals, btAlignedObjectArray<int>& indices, struct CommonFileIOInterface* fileIO);
void* m_userData;
int m_userIndex;
int m_objectIndex;
int m_linkIndex;
bool m_doubleSided;
};
class TinyRenderer
{
public:
static void renderObjectDepth(TinyRenderObjectData& renderData);
static void renderObject(TinyRenderObjectData& renderData);
};
#endif // TINY_RENDERER_Hbla

View file

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

View file

@ -0,0 +1,327 @@
#ifndef __GEOMETRY_H__
#define __GEOMETRY_H__
#include <cmath>
#include <cassert>
#include <stdlib.h>
namespace TinyRender
{
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, double> Vec2d;
typedef vec<2, int> Vec2i;
typedef vec<3, float> Vec3f;
typedef vec<3, double> Vec3d;
typedef vec<3, int> Vec3i;
typedef vec<4, float> Vec4f;
typedef mat<4, 4, float> Matrix;
}
#endif //__GEOMETRY_H__

View file

@ -0,0 +1,240 @@
#include "OpenGLWindow/SimpleOpenGL3App.h"
#include "Bullet3Common/b3Quaternion.h"
#include "Bullet3Common/b3CommandLineArgs.h"
#include "Bullet3Common/b3Transform.h"
#include "Utils/b3BulletDefaultFileIO.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"
#include "our_gl.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;
TGAImage rgbColorBuffer(gWidth, gHeight, TGAImage::RGB);
b3AlignedObjectArray<float> depthBuffer;
depthBuffer.resize(gWidth * gHeight);
TinyRenderObjectData renderData(rgbColorBuffer, depthBuffer); //, "african_head/african_head.obj");//floor.obj");
b3BulletDefaultFileIO fileIO;
//renderData.loadModel("african_head/african_head.obj", &fileIO);
renderData.loadModel("floor.obj",&fileIO);
//renderData.createCube(1,1,1);
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);
float color[4] = {1,1,1,1};
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();
///clear the color and z (depth) buffer
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);
renderData.m_depthBuffer[x + y * textureWidth] = -1e30f;
}
}
float projMat[16];
app->m_instancingRenderer->getActiveCamera()->getCameraProjectionMatrix(projMat);
float viewMat[16];
app->m_instancingRenderer->getActiveCamera()->getCameraViewMatrix(viewMat);
B3_ATTRIBUTE_ALIGNED16(float modelMat[16]);
//sync the object transform
b3Transform tr;
tr.setIdentity();
static float posUp = 0.f;
// posUp += 0.001;
b3Vector3 org = b3MakeVector3(0, 0, posUp);
tr.setOrigin(org);
tr.getOpenGLMatrix(modelMat);
TinyRender::Vec3f eye(1,1,3);
TinyRender::Vec3f center(0,0,0);
TinyRender::Vec3f up(0,1,0);
renderData.m_viewMatrix = TinyRender::lookat(eye, center, up);
renderData.m_viewportMatrix = TinyRender::viewport(gWidth/8, gHeight/8, gWidth*3/4, gHeight*3/4);
renderData.m_projectionMatrix = TinyRender::projection(-1.f/(eye-center).norm());
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_modelMatrix[i][j] = modelMat[i + 4 * j];
}
}
//render the object
float color2[4] = { 1,1,1,1 };
renderData.m_model->setColorRGBA(color2);
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] = 255;
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)
{
TGAColor color = renderData.m_rgbColorBuffer.get(x, y);
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);
float colorRGBA[4] = { 1,1,1,1 };
app->drawText(bla, 10, 10,1, colorRGBA);
app->swapBuffer();
} while (!app->m_window->requestedExit());
delete app;
return 0;
}

View file

@ -0,0 +1,217 @@
#include "model.h"
#include <string.h> // memcpy
#include <cmath>
#include <fstream>
#include <iostream>
#include <sstream>
#include "Bullet3Common/b3Logging.h"
namespace TinyRender
{
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() : verts_(), faces_(), norms_(), uv_(), diffusemap_(), normalmap_(), specularmap_()
{
}
void Model::setDiffuseTextureFromData(unsigned char *textureImage, int textureWidth, int textureHeight)
{
{
B3_PROFILE("new TGAImage");
diffusemap_ = TGAImage(textureWidth, textureHeight, TGAImage::RGB);
}
TGAColor color;
color.bgra[3] = 255;
color.bytespp = 3;
{
B3_PROFILE("copy texels");
memcpy(diffusemap_.buffer(), textureImage, textureHeight * textureWidth * 3);
}
{
B3_PROFILE("flip_vertically");
diffusemap_.flip_vertically();
}
}
void Model::loadDiffuseTexture(const char *relativeFileName)
{
diffusemap_.read_tga_file(relativeFileName);
}
void Model::reserveMemory(int numVertices, int numIndices)
{
verts_.reserve(numVertices);
norms_.reserve(numVertices);
uv_.reserve(numVertices);
faces_.reserve(numIndices);
}
void Model::addVertex(float x, float y, float z, float normalX, float normalY, float normalZ, float u, float v)
{
verts_.push_back(Vec3f(x, y, z));
norms_.push_back(Vec3f(normalX, normalY, normalZ));
uv_.push_back(Vec2f(u, v));
}
void Model::addTriangle(int vertexposIndex0, int normalIndex0, int uvIndex0,
int vertexposIndex1, int normalIndex1, int uvIndex1,
int vertexposIndex2, int normalIndex2, int uvIndex2)
{
std::vector<Vec3i> f;
f.push_back(Vec3i(vertexposIndex0, normalIndex0, uvIndex0));
f.push_back(Vec3i(vertexposIndex1, normalIndex1, uvIndex1));
f.push_back(Vec3i(vertexposIndex2, normalIndex2, uvIndex2));
faces_.push_back(f);
}
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;
face.reserve((int)faces_[idx].size());
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)
{
if (diffusemap_.get_width() && diffusemap_.get_height())
{
double val;
// bool repeat = true;
// if (repeat)
{
uvf[0] = std::modf(uvf[0], &val);
if (uvf[0] < 0)
{
uvf[0] = uvf[0] + 1;
}
uvf[1] = std::modf(uvf[1], &val);
if (uvf[1] < 0)
{
uvf[1] = uvf[1] + 1;
}
}
Vec2i uv(uvf[0] * diffusemap_.get_width(), uvf[1] * diffusemap_.get_height());
return diffusemap_.get(uv[0], uv[1]);
}
return TGAColor(255, 255, 255, 255);
}
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)
{
if (specularmap_.get_width() && specularmap_.get_height())
{
Vec2i uv(uvf[0] * specularmap_.get_width(), uvf[1] * specularmap_.get_height());
return specularmap_.get(uv[0], uv[1])[0] / 1.f;
}
return 2.0;
}
Vec3f Model::normal(int iface, int nthvert)
{
int idx = faces_[iface][nthvert][2];
return norms_[idx].normalize();
}
}

View file

@ -0,0 +1,79 @@
#ifndef __MODEL_H__
#define __MODEL_H__
#include <vector>
#include <string>
#include "geometry.h"
#include "tgaimage.h"
namespace TinyRender
{
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_;
Vec4f m_colorRGBA;
void load_texture(std::string filename, const char* suffix, TGAImage& img);
public:
Model(const char* filename);
Model();
void setColorRGBA(const float rgba[4])
{
for (int i = 0; i < 4; i++)
m_colorRGBA[i] = rgba[i];
}
const Vec4f& getColorRGBA() const
{
return m_colorRGBA;
}
void loadDiffuseTexture(const char* relativeFileName);
void setDiffuseTextureFromData(unsigned char* textureImage, int textureWidth, int textureHeight);
void reserveMemory(int numVertices, int numIndices);
void addVertex(float x, float y, float z, float normalX, float normalY, float normalZ, float u, float v);
void addTriangle(int vertexposIndex0, int normalIndex0, int uvIndex0,
int vertexposIndex1, int normalIndex1, int uvIndex1,
int vertexposIndex2, int normalIndex2, int uvIndex2);
~Model();
int nverts();
int nnormals()
{
return norms_.size();
}
int nfaces();
Vec3f normal(int iface, int nthvert);
Vec3f normal(Vec2f uv);
Vec3f vert(int i);
Vec3f vert(int iface, int nthvert);
Vec3f* readWriteVertices()
{
if (verts_.size() == 0)
return 0;
return &verts_[0];
}
Vec3f* readWriteNormals()
{
if (norms_.size() == 0)
return 0;
return &norms_[0];
}
Vec2f uv(int iface, int nthvert);
TGAColor diffuse(Vec2f uv);
float specular(Vec2f uv);
std::vector<int> face(int idx);
};
}
#endif //__MODEL_H__

View file

@ -0,0 +1,221 @@
#include <cmath>
#include <limits>
#include <cstdlib>
#include "our_gl.h"
#include "Bullet3Common/b3MinMax.h"
namespace TinyRender
{
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] = .5f;
Viewport[0][0] = w / 2.f;
Viewport[1][1] = h / 2.f;
Viewport[2][2] = .5f;
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 f = (center - eye).normalize();
Vec3f u = up.normalize();
Vec3f s = cross(f, u).normalize();
u = cross(s, f);
Matrix ModelView;
ModelView[0][0] = s.x;
ModelView[0][1] = s.y;
ModelView[0][2] = s.z;
ModelView[1][0] = u.x;
ModelView[1][1] = u.y;
ModelView[1][2] = u.z;
ModelView[2][0] = -f.x;
ModelView[2][1] = -f.y;
ModelView[2][2] = -f.z;
ModelView[3][0] = 0.f;
ModelView[3][1] = 0.f;
ModelView[3][2] = 0.f;
ModelView[0][3] = -(s[0] * eye[0] + s[1] * eye[1] + s[2] * eye[2]);
ModelView[1][3] = -(u[0] * eye[0] + u[1] * eye[1] + u[2] * eye[2]);
ModelView[2][3] = f[0] * eye[0] + f[1] * eye[1] + f[2] * eye[2];
ModelView[3][3] = 1.f;
return ModelView;
}
Vec3d barycentric(Vec2f A1, Vec2f B1, Vec2f C1, Vec2f P1)
{
Vec2d A(A1.x, A1.y);
Vec2d B(B1.x, B1.y);
Vec2d C(C1.x, C1.y);
Vec2d P(P1.x, P1.y);;
Vec3d 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];
}
Vec3d 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 Vec3d(1. - (u.x + u.y) / u.z, u.y / u.z, u.x / u.z);
return Vec3d(-1., 1., 1.); // in this case generate negative coordinates, it will be thrown away by the rasterizator
}
void triangleClipped(mat<4, 3, float> &clipc, mat<4, 3, float> &orgClipc, IShader &shader, TGAImage &image, float *zbuffer, const Matrix &viewPortMatrix)
{
triangleClipped(clipc, orgClipc, shader, image, zbuffer, 0, viewPortMatrix, 0);
}
void triangleClipped(mat<4, 3, float> &clipc, mat<4, 3, float> &orgClipc, IShader &shader, TGAImage &image, float *zbuffer, int *segmentationMaskBuffer, const Matrix &viewPortMatrix, int objectAndLinkIndex)
{
mat<3, 4, float> screenSpacePts = (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>(screenSpacePts[i] / screenSpacePts[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;
mat<3, 4, float> orgScreenSpacePts = (viewPortMatrix * orgClipc).transpose(); // transposed to ease access to each of the points
mat<3, 2, float> orgPts2;
for (int i = 0; i < 3; i++)
{
orgPts2[i] = proj<2>(orgScreenSpacePts[i] / orgScreenSpacePts[i][3]);
}
for (P.x = bboxmin.x; P.x <= bboxmax.x; P.x++)
{
for (P.y = bboxmin.y; P.y <= bboxmax.y; P.y++)
{
double frag_depth = 0;
{
Vec3d bc_screen = barycentric(pts2[0], pts2[1], pts2[2], P);
Vec3d bc_clip = Vec3d(bc_screen.x / screenSpacePts[0][3], bc_screen.y / screenSpacePts[1][3], bc_screen.z / screenSpacePts[2][3]);
bc_clip = bc_clip / (bc_clip.x + bc_clip.y + bc_clip.z);
Vec3d clipd(clipc[2].x, clipc[2].y, clipc[2].z);
frag_depth = -1. * (clipd * 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;
}
Vec3d bc_screen2 = barycentric(orgPts2[0], orgPts2[1], orgPts2[2], P);
Vec3d bc_clip2 = Vec3d(bc_screen2.x / orgScreenSpacePts[0][3], bc_screen2.y / orgScreenSpacePts[1][3], bc_screen2.z / orgScreenSpacePts[2][3]);
bc_clip2 = bc_clip2 / (bc_clip2.x + bc_clip2.y + bc_clip2.z);
Vec3d orgClipd(orgClipc[2].x, orgClipc[2].y, orgClipc[2].z);
double frag_depth2 = -1. * (orgClipd * bc_clip2);
Vec3f bc_clip2f(bc_clip2.x, bc_clip2.y, bc_clip2.z);
bool discard = shader.fragment(bc_clip2f, color);
if (!discard)
{
zbuffer[P.x + P.y * image.get_width()] = frag_depth;
if (segmentationMaskBuffer)
{
segmentationMaskBuffer[P.x + P.y * image.get_width()] = objectAndLinkIndex;
}
image.set(P.x, P.y, color);
}
}
}
}
void triangle(mat<4, 3, float> &clipc, IShader &shader, TGAImage &image, float *zbuffer, const Matrix &viewPortMatrix)
{
triangle(clipc, shader, image, zbuffer, 0, viewPortMatrix, 0);
}
void triangle(mat<4, 3, float> &clipc, IShader &shader, TGAImage &image, float *zbuffer, int *segmentationMaskBuffer, const Matrix &viewPortMatrix, int objectAndLinkIndex)
{
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++)
{
Vec3d bc_screen = barycentric(pts2[0], pts2[1], pts2[2], P);
Vec3d bc_clip = Vec3d(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);
Vec3d clipd(clipc[2].x, clipc[2].y, clipc[2].z);
double frag_depth = -1. * (clipd * 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;
Vec3f bc_clipf(bc_clip.x, bc_clip.y, bc_clip.z);
bool discard = shader.fragment(bc_clipf, color);
if (frag_depth < -shader.m_farPlane)
discard = true;
if (frag_depth > shader.m_nearPlane)
discard = true;
if (!discard)
{
zbuffer[P.x + P.y * image.get_width()] = frag_depth;
if (segmentationMaskBuffer)
{
segmentationMaskBuffer[P.x + P.y * image.get_width()] = objectAndLinkIndex;
}
image.set(P.x, P.y, color);
}
}
}
}
}

View file

@ -0,0 +1,27 @@
#ifndef __OUR_GL_H__
#define __OUR_GL_H__
#include "tgaimage.h"
#include "geometry.h"
namespace TinyRender
{
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
{
float m_nearPlane;
float m_farPlane;
virtual ~IShader();
virtual Vec4f vertex(int iface, int nthvert) = 0;
virtual bool fragment(Vec3f bar, TGAColor &color) = 0;
};
void triangle(mat<4, 3, float> &pts, IShader &shader, TGAImage &image, float *zbuffer, const Matrix &viewPortMatrix);
void triangle(mat<4, 3, float> &pts, IShader &shader, TGAImage &image, float *zbuffer, int *segmentationMaskBuffer, const Matrix &viewPortMatrix, int objectIndex);
void triangleClipped(mat<4, 3, float> &clippedPts, mat<4, 3, float> &pts, IShader &shader, TGAImage &image, float *zbuffer, const Matrix &viewPortMatrix);
void triangleClipped(mat<4, 3, float> &clippedPts, mat<4, 3, float> &pts, IShader &shader, TGAImage &image, float *zbuffer, int *segmentationMaskBuffer, const Matrix &viewPortMatrix, int objectIndex);
}
#endif //__OUR_GL_H__

View file

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

View file

@ -0,0 +1,443 @@
#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) const
{
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
{
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) const
{
if (x < 0)
{
x = 0;
}
if (y < 0)
{
y = 0;
}
if (x >= width)
{
x = width - 1;
}
if (y >= height)
{
y = height - 1;
}
if (!data || x < 0 || y < 0 || x >= width || y >= height)
{
return TGAColor(128.f, 128.f, 128.f, 255.f);
}
return TGAColor(data + (x + y * width) * bytespp, bytespp);
}
bool TGAImage::set(int x, int y, TGAColor &c)
{
if (!data || x < 0 || y < 0 || x >= width || y >= height)
{
return false;
}
memcpy(data + (x + y * width) * bytespp, c.bgra, bytespp);
return true;
}
bool TGAImage::set(int x, int y, const TGAColor &c)
{
if (!data || x < 0 || y < 0 || x >= width || y >= height)
{
return false;
}
memcpy(data + (x + y * width) * bytespp, c.bgra, bytespp);
return true;
}
int TGAImage::get_bytespp()
{
return bytespp;
}
int TGAImage::get_width()
{
return width;
}
int TGAImage::get_height()
{
return height;
}
bool TGAImage::flip_horizontally()
{
if (!data) return false;
int half = width >> 1;
for (int i = 0; i < half; i++)
{
for (int j = 0; j < height; j++)
{
TGAColor c1 = get(i, j);
TGAColor c2 = get(width - 1 - i, j);
set(i, j, c2);
set(width - 1 - i, j, c1);
}
}
return true;
}
bool TGAImage::flip_vertically()
{
if (!data) return false;
unsigned long bytes_per_line = width * bytespp;
unsigned char *line = new unsigned char[bytes_per_line];
int half = height >> 1;
for (int j = 0; j < half; j++)
{
unsigned long l1 = j * bytes_per_line;
unsigned long l2 = (height - 1 - j) * bytes_per_line;
memmove((void *)line, (void *)(data + l1), bytes_per_line);
memmove((void *)(data + l1), (void *)(data + l2), bytes_per_line);
memmove((void *)(data + l2), (void *)line, bytes_per_line);
}
delete[] line;
return true;
}
unsigned char *TGAImage::buffer()
{
return data;
}
void TGAImage::clear()
{
memset((void *)data, 0, width * height * bytespp);
}
bool TGAImage::scale(int w, int h)
{
if (w <= 0 || h <= 0 || !data) return false;
unsigned char *tdata = new unsigned char[w * h * bytespp];
int nscanline = 0;
int oscanline = 0;
int erry = 0;
unsigned long nlinebytes = w * bytespp;
unsigned long olinebytes = width * bytespp;
for (int j = 0; j < height; j++)
{
int errx = width - w;
int nx = -bytespp;
int ox = -bytespp;
for (int i = 0; i < width; i++)
{
ox += bytespp;
errx += w;
while (errx >= (int)width)
{
errx -= width;
nx += bytespp;
memcpy(tdata + nscanline + nx, data + oscanline + ox, bytespp);
}
}
erry += h;
oscanline += olinebytes;
while (erry >= (int)height)
{
if (erry >= (int)height << 1) // it means we jump over a scanline
memcpy(tdata + nscanline + nlinebytes, tdata + nscanline, nlinebytes);
erry -= height;
nscanline += nlinebytes;
}
}
delete[] data;
data = tdata;
width = w;
height = h;
return true;
}

View file

@ -0,0 +1,112 @@
#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() : 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) : bytespp(4)
{
bgra[0] = B;
bgra[1] = G;
bgra[2] = R;
bgra[3] = A;
}
TGAColor(unsigned char v) : bytespp(1)
{
for (int i = 0; i < 4; i++) bgra[i] = 0;
bgra[0] = v;
}
TGAColor(const unsigned char *p, unsigned char bpp) : 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) const;
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) const;
bool flip_horizontally();
bool flip_vertically();
bool scale(int w, int h);
TGAColor get(int x, int y) const;
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__