Start moving model around in 3d space

This commit is contained in:
Doyle Thai 2017-06-08 01:18:55 +10:00
parent 4c5f8d43a0
commit ee31383906
14 changed files with 1329 additions and 365 deletions

View File

@ -981,11 +981,19 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
DTRAsset_LoadBitmap(input->api, assetStack,
tempStack, &state->bitmap, "tree00.bmp");
#if 1
if (DTRAsset_LoadWavefrontObj(input->api, assetStack, &state->mesh, "african_head.obj"))
{
DTRAsset_LoadBitmap(input->api, assetStack, tempStack, &state->mesh.tex,
"african_head_diffuse.tga");
}
#else
if (DTRAsset_LoadWavefrontObj(input->api, assetStack, &state->mesh, "chalet.obj"))
{
DTRAsset_LoadBitmap(input->api, assetStack, tempStack, &state->mesh.tex,
"chalet.jpg");
}
#endif
////////////////////////////////////////////////////////////////////////
// Init Debug
@ -993,7 +1001,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
if (DTR_DEBUG)
{
DebugTestStrToF32Converter();
DTRDebug_TestMeshFaceAndVertexParser(&state->mesh);
// DTRDebug_TestMeshFaceAndVertexParser(&state->mesh);
DqnTempMemStack tmp = DqnMemStack_BeginTempRegion(&memory->tempStack);
DTRBitmap test = {};
@ -1048,7 +1056,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
DTRRenderTransform rotatingXform = DTRRender_DefaultTriangleTransform();
rotatingXform.rotation = rotation;
if (1)
if (0)
{
DTRDebug_BeginCycleCount("DTR_Update_RenderPrimitiveTriangles",
DTRDebugCycleCount_DTR_Update_RenderPrimitiveTriangles);
@ -1063,16 +1071,23 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
if (1)
{
LOCAL_PERSIST bool runTinyRendererOnce = true;
if (1 && runTinyRendererOnce)
{
DTRDebug_RunTinyRenderer();
runTinyRendererOnce = false;
}
DTRDebug_BeginCycleCount("DTR_Update_RenderModel", DTRDebugCycleCount_DTR_Update_RenderModel);
////////////////////////////////////////////////////////////////////////
// Draw Loaded Model
////////////////////////////////////////////////////////////////////////
const DqnV3 LIGHT = DqnV3_3i(0, 0, -1);
const f32 MODEL_SCALE = DQN_MIN(renderBuffer.width, renderBuffer.height) * 0.5f;
const f32 MODEL_SCALE = 1;
DTRMesh *const mesh = &state->mesh;
DqnV3 modelP = DqnV3_3f(renderBuffer.width * 0.5f, renderBuffer.height * 0.5f, 0);
DqnV3 modelP = DqnV3_3f(0, 0, 0);
DTRRender_Mesh(&renderBuffer, mesh, modelP, MODEL_SCALE, LIGHT);
DTRRender_Mesh(&renderBuffer, mesh, modelP, MODEL_SCALE, LIGHT, input->deltaForFrame);
DTRDebug_EndCycleCount(DTRDebugCycleCount_DTR_Update_RenderModel);
}
}

View File

@ -162,6 +162,31 @@ WavefModelFaceInit(i32 capacity = 3, DqnMemAPI memAPI = DqnMemAPI_DefaultUseCall
return result;
}
FILE_SCOPE size_t FindFirstNewlineFeedChar(char *ptr)
{
size_t offset = 0;
while (ptr && (*ptr != '\n' && *ptr != '\r'))
{
offset++;
ptr++;
}
return offset;
}
FILE_SCOPE size_t FindFirstCharNotLinefeedOrSpace(char *ptr)
{
size_t offset = 0;
while (ptr && (*ptr == ' ' || *ptr == '\n' || *ptr == '\r'))
{
offset++;
ptr++;
}
return offset;
}
bool DTRAsset_LoadWavefrontObj(const PlatformAPI api, DqnMemStack *const memStack,
DTRMesh *const mesh, const char *const path)
{
@ -246,7 +271,7 @@ bool DTRAsset_LoadWavefrontObj(const PlatformAPI api, DqnMemStack *const memStac
for (;;)
{
char *f32StartPtr = scan;
for (; *scan != ' ' && *scan != '\n';)
for (; *scan != ' ' && *scan != '\n' && *scan != '\r';)
{
DQN_ASSERT(DqnChar_IsDigit(*scan) || (*scan == '.') || (*scan == '-') ||
*scan == 'e');
@ -257,13 +282,12 @@ bool DTRAsset_LoadWavefrontObj(const PlatformAPI api, DqnMemStack *const memStac
v4.e[vIndex++] = Dqn_StrToF32(f32StartPtr, f32Len);
DQN_ASSERT(vIndex < DQN_ARRAY_COUNT(v4.e));
while (scan && (*scan == ' ' || *scan == '\n')) scan++;
scan += FindFirstCharNotLinefeedOrSpace(scan);
if (!scan) break;
if (!(DqnChar_IsDigit(*scan) || *scan == '-')) break;
}
DQN_ASSERT(vIndex == 3 || vIndex == 4);
DQN_ASSERT(vIndex >= 2 && vIndex <= 4);
if (type == WavefVertexType_Geometric)
{
DqnArray_Push(&obj->geometryArray, v4);
@ -325,7 +349,7 @@ bool DTRAsset_LoadWavefrontObj(const PlatformAPI api, DqnMemStack *const memStac
case 'f':
{
scan++;
while (scan && (*scan == ' ' || *scan == '\n')) scan++;
scan += FindFirstCharNotLinefeedOrSpace(scan);
if (!scan) continue;
WavefModelFace face = WavefModelFaceInit(3, memAPI);
@ -373,9 +397,7 @@ bool DTRAsset_LoadWavefrontObj(const PlatformAPI api, DqnMemStack *const memStac
if (scan)
{
// Move to next "non-empty" character
while (scan && (*scan == ' ' || *scan == '\n'))
scan++;
scan += FindFirstCharNotLinefeedOrSpace(scan);
// If it isn't a digit, then we've read all the
// vertexes for this face
@ -401,14 +423,12 @@ bool DTRAsset_LoadWavefrontObj(const PlatformAPI api, DqnMemStack *const memStac
case 'g':
{
scan++;
while (scan && (*scan == ' ' || *scan == '\n')) scan++;
scan += FindFirstCharNotLinefeedOrSpace(scan);
if (!scan) continue;
// Iterate to end of the name, i.e. move ptr to first space
char *namePtr = scan;
while (scan && (*scan != ' ' && *scan != '\n'))
scan++;
scan += FindFirstCharNotLinefeedOrSpace(scan);
if (scan)
{
@ -428,8 +448,7 @@ bool DTRAsset_LoadWavefrontObj(const PlatformAPI api, DqnMemStack *const memStac
obj->groupName[obj->groupNameIndex - 1][i] = namePtr[i];
#endif
while (scan && (*scan == ' ' || *scan == '\n'))
scan++;
scan += FindFirstCharNotLinefeedOrSpace(scan);
}
}
break;
@ -439,14 +458,13 @@ bool DTRAsset_LoadWavefrontObj(const PlatformAPI api, DqnMemStack *const memStac
// not to be used it can be specified as "off" or a value of 0.
case 's':
{
// Advance to first non space char after identifier
scan++;
while (scan && *scan == ' ' || *scan == '\n') scan++;
scan += FindFirstCharNotLinefeedOrSpace(scan);
if (scan && DqnChar_IsDigit(*scan))
{
char *numStartPtr = scan;
while (scan && (*scan != ' ' && *scan != '\n'))
while (scan && (*scan != ' ' && *scan != '\n' && *scan != '\r'))
{
DQN_ASSERT(DqnChar_IsDigit(*scan));
scan++;
@ -457,26 +475,25 @@ bool DTRAsset_LoadWavefrontObj(const PlatformAPI api, DqnMemStack *const memStac
obj->groupSmoothing = groupSmoothing;
}
while (scan && *scan == ' ' || *scan == '\n') scan++;
scan += FindFirstCharNotLinefeedOrSpace(scan);
}
break;
// Comment
case '#':
{
// Skip comment line until new line
while (scan && *scan != '\n')
scan++;
// Skip new lines and any leading white spaces
while (scan && (*scan == '\n' || *scan == ' '))
scan++;
scan += FindFirstNewlineFeedChar(scan);
scan += FindFirstCharNotLinefeedOrSpace(scan);
}
break;
default:
{
DQN_ASSERT(DQN_INVALID_CODE_PATH);
// TODO(doyle): Ignore unrecognised items.
// DQN_ASSERT(DQN_INVALID_CODE_PATH);
scan += FindFirstNewlineFeedChar(scan);
scan += FindFirstCharNotLinefeedOrSpace(scan);
}
break;
}
@ -723,9 +740,6 @@ FILE_SCOPE void *STBImageMalloc(size_t size)
#define STBI_FREE(ptr) STBImageFree(ptr)
#define STBI_NO_STDIO
#define STBI_ONLY_PNG
#define STBI_ONLY_TGA
#define STBI_ONLY_BMP
#define STB_IMAGE_IMPLEMENTATION
#include "external/stb_image.h"
@ -755,7 +769,12 @@ bool DTRAsset_LoadBitmap(const PlatformAPI api, DqnMemStack *const memStack,
bitmap->bytesPerPixel = FORCE_4_BPP;
u8 *pixels = stbi_load_from_memory(rawData, (i32)file.size, &bitmap->dim.w, &bitmap->dim.h,
NULL, FORCE_4_BPP);
if (!pixels) goto cleanup;
if (!pixels)
{
const char *failReason = stbi_failure_reason();
api.Print(failReason);
goto cleanup;
}
result = true;
// TODO(doyle): See above. Since we use temp stack we can allocate straight into the AssetStack.

View File

@ -6,9 +6,9 @@
#include "dqn.h"
#include "external/tests/tinyrenderer/geometry.h"
#include "external/tests/tinyrenderer/model.cpp"
#include <vector>
#if DTR_DEBUG
#include "external/tests/tinyrenderer/tinyrenderer.cpp"
#endif
DTRDebug globalDebug;
void DTRDebug_TestMeshFaceAndVertexParser(DTRMesh *const mesh)
@ -16,12 +16,12 @@ void DTRDebug_TestMeshFaceAndVertexParser(DTRMesh *const mesh)
if (DTR_DEBUG)
{
if (!mesh) DQN_ASSERT(DQN_INVALID_CODE_PATH);
Model model = Model("african_head.obj");
Model tmpModel = Model("african_head.obj");
DQN_ASSERT((i64)mesh->numFaces == model.nfaces());
for (i32 i = 0; i < model.nfaces(); i++)
DQN_ASSERT((i64)mesh->numFaces == tmpModel.nfaces());
for (i32 i = 0; i < tmpModel.nfaces(); i++)
{
std::vector<i32> correctFace = model.face(i);
std::vector<i32> correctFace = tmpModel.face(i);
DTRMeshFace *myFace = &mesh->faces[i];
DQN_ASSERT(myFace->numVertexIndex == correctFace.size());
@ -31,7 +31,7 @@ void DTRDebug_TestMeshFaceAndVertexParser(DTRMesh *const mesh)
// Ensure the vertex index references are correct per face
DQN_ASSERT(myFace->vertexIndex[j] == correctFace[j]);
Vec3f tmp = model.vert(correctFace[j]);
Vec3f tmp = tmpModel.vert(correctFace[j]);
DqnV3 correctVertex = DqnV3_3f(tmp[0], tmp[1], tmp[2]);
DqnV3 myVertex = (mesh->vertexes[myFace->vertexIndex[j]]).xyz;
@ -92,6 +92,14 @@ void DTRDebug_DumpZBuffer(DTRRenderBuffer *const renderBuffer, DqnMemStack *cons
}
}
void DTRDebug_RunTinyRenderer()
{
if (DTR_DEBUG)
{
tinyrenderer(0, NULL);
}
}
void DTRDebug_PushText(const char *const formatStr, ...)
{
if (DTR_DEBUG)

View File

@ -106,6 +106,7 @@ extern DTRDebug globalDebug;
void DTRDebug_TestMeshFaceAndVertexParser(struct DTRMesh *const mesh);
void DTRDebug_DumpZBuffer (struct DTRRenderBuffer *const renderBuffer, struct DqnMemStack *const transMemStack);
void DTRDebug_RunTinyRenderer ();
void DTRDebug_PushText (const char *const formatStr, ...);
void DTRDebug_Update (struct DTRState *const state, struct DTRRenderBuffer *const renderBuffer, struct PlatformInput *const input, struct PlatformMemory *const memory);
void inline DTRDebug_BeginCycleCount (char *title, enum DTRDebugCycleCount tag);

View File

@ -752,14 +752,6 @@ FILE_SCOPE void DebugRenderMarkers(DTRRenderBuffer *const renderBuffer, const Dq
}
}
FILE_SCOPE void SIMDTexturedTriangle(DTRRenderBuffer *const renderBuffer, const DqnV3 p1,
const DqnV3 p2, const DqnV3 p3, const DqnV2 uv1,
const DqnV2 uv2, const DqnV2 uv3,
const DTRBitmap *const texture, const DqnV4 color,
const DqnV2i min, const DqnV2i max)
{
DTR_DEBUG_EP_TIMED_FUNCTION();
#define DEBUG_SIMD_AUTO_CHOOSE_BEGIN_CYCLE_COUNT(type) \
do \
{ \
@ -778,6 +770,58 @@ FILE_SCOPE void SIMDTexturedTriangle(DTRRenderBuffer *const renderBuffer, const
DTRDebug_EndCycleCount(DTRDebugCycleCount_SIMD##type); \
} while (0)
FILE_SCOPE void SIMDRasteriseTrianglePixel(DTRRenderBuffer *const renderBuffer,
const DTRBitmap *const texture, const i32 posX,
const i32 posY, const i32 maxX, const DqnV2 uv1,
const DqnV2 uv2SubUv1, const DqnV2 uv3SubUv1,
const __m128 simdColor, const __m128 triangleZ,
const __m128 signedArea,
const __m128 invSignedAreaParallelogram_4x)
{
const __m128 ZERO_4X = _mm_set_ps1(0.0f);
const u32 IS_GREATER_MASK = 0xF;
const u32 zBufferPitch = renderBuffer->width;
// Rasterise buffer(X, Y) pixel
{
__m128 isGreater = _mm_cmpge_ps(signedArea, ZERO_4X);
i32 isGreaterResult = _mm_movemask_ps(isGreater);
if ((isGreaterResult & IS_GREATER_MASK) == IS_GREATER_MASK && posX < maxX)
{
DEBUG_SIMD_AUTO_CHOOSE_BEGIN_CYCLE_COUNT(Triangle_RasterisePixel);
__m128 barycentric = _mm_mul_ps(signedArea, invSignedAreaParallelogram_4x);
__m128 barycentricZ = _mm_mul_ps(triangleZ, barycentric);
i32 zBufferIndex = posX + (posY * zBufferPitch);
f32 pixelZValue =
((f32 *)&barycentricZ)[0] + ((f32 *)&barycentricZ)[1] + ((f32 *)&barycentricZ)[2];
f32 currZValue = renderBuffer->zBuffer[zBufferIndex];
if (pixelZValue > currZValue)
{
renderBuffer->zBuffer[zBufferIndex] = pixelZValue;
__m128 finalColor = simdColor;
if (texture)
{
__m128 texSampledColor = SIMDSampleTextureForTriangle(texture, uv1, uv2SubUv1,
uv3SubUv1, barycentric);
finalColor = _mm_mul_ps(texSampledColor, simdColor);
}
SIMDSetPixel(renderBuffer, posX, posY, finalColor, ColorSpace_Linear);
}
DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle_RasterisePixel);
}
}
}
FILE_SCOPE void SIMDTriangle(DTRRenderBuffer *const renderBuffer, const DqnV3 p1, const DqnV3 p2,
const DqnV3 p3, const DqnV2 uv1, const DqnV2 uv2, const DqnV2 uv3,
const DTRBitmap *const texture, const DqnV4 color, const DqnV2i min,
const DqnV2i max)
{
DTR_DEBUG_EP_TIMED_FUNCTION();
DEBUG_SIMD_AUTO_CHOOSE_BEGIN_CYCLE_COUNT(Triangle);
DEBUG_SIMD_AUTO_CHOOSE_BEGIN_CYCLE_COUNT(Triangle_Preamble);
@ -794,10 +838,6 @@ FILE_SCOPE void SIMDTexturedTriangle(DTRRenderBuffer *const renderBuffer, const
const u32 NUM_X_PIXELS_TO_SIMD = 2;
const u32 NUM_Y_PIXELS_TO_SIMD = 1;
const __m128 INV255_4X = _mm_set_ps1(1.0f / 255.0f);
const __m128 ZERO_4X = _mm_set_ps1(0.0f);
const u32 IS_GREATER_MASK = 0xF;
// SignedArea: _mm_set_ps(unused, p3, p2, p1) ie 0=p1, 1=p1, 2=p3, 3=unused
__m128 signedAreaPixel1;
__m128 signedAreaPixel2;
@ -809,9 +849,8 @@ FILE_SCOPE void SIMDTexturedTriangle(DTRRenderBuffer *const renderBuffer, const
__m128 triangleZ = _mm_set_ps(0, p3.z, p2.z, p1.z);
{
DEBUG_SIMD_AUTO_CHOOSE_BEGIN_CYCLE_COUNT(Triangle_Preamble_SArea);
DTR_DEBUG_EP_TIMED_BLOCK("SIMDTexturedTriangle_Preamble_SArea");
DTR_DEBUG_EP_TIMED_BLOCK("SIMDTriangle_Preamble_SArea");
DqnV2 startP = DqnV2_V2i(min);
#if 1
f32 signedArea1Start = Triangle2TimesSignedArea(p2.xy, p3.xy, startP);
f32 signedArea1DeltaX = p2.y - p3.y;
f32 signedArea1DeltaY = p3.x - p2.x;
@ -823,20 +862,6 @@ FILE_SCOPE void SIMDTexturedTriangle(DTRRenderBuffer *const renderBuffer, const
f32 signedArea3Start = Triangle2TimesSignedArea(p1.xy, p2.xy, startP);
f32 signedArea3DeltaX = p1.y - p2.y;
f32 signedArea3DeltaY = p2.x - p1.x;
#else
f32 signedArea1Start = ((p3.x - p2.x) * (startP.y - p2.y)) - ((p3.y - p2.y) * (startP.x - p2.x));
f32 signedArea1DeltaX = p2.y - p3.y;
f32 signedArea1DeltaY = p3.x - p2.x;
f32 signedArea2Start = ((p1.x - p3.x) * (startP.y - p3.y)) - ((p1.y - p3.y) * (startP.x - p3.x));
f32 signedArea2DeltaX = p3.y - p1.y;
f32 signedArea2DeltaY = p1.x - p3.x;
f32 signedArea3Start = ((p2.x - p1.x) * (startP.y - p1.y)) - ((p2.y - p1.y) * (startP.x - p1.x));
f32 signedArea3DeltaX = p1.y - p2.y;
f32 signedArea3DeltaY = p2.x - p1.x;
#endif
DTR_DEBUG_EP_TIMED_END_BLOCK();
DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle_Preamble_SArea);
@ -867,11 +892,16 @@ FILE_SCOPE void SIMDTexturedTriangle(DTRRenderBuffer *const renderBuffer, const
const DqnV2 uv2SubUv1 = uv2 - uv1;
const DqnV2 uv3SubUv1 = uv3 - uv1;
const u32 texturePitch = (texture) ? (texture->bytesPerPixel * texture->dim.w) : 0;
const u8 *const texturePtr = (texture) ? (texture->memory) : NULL;
const u32 zBufferPitch = renderBuffer->width;
#define UNROLL_LOOP 1
DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle_Preamble);
#if UNROLL_LOOP
const __m128 ZERO_4X = _mm_set_ps1(0.0f);
const u32 IS_GREATER_MASK = 0xF;
const u32 zBufferPitch = renderBuffer->width;
#endif
////////////////////////////////////////////////////////////////////////////
// Scan and Render
////////////////////////////////////////////////////////////////////////////
@ -883,7 +913,7 @@ FILE_SCOPE void SIMDTexturedTriangle(DTRRenderBuffer *const renderBuffer, const
for (i32 bufferX = min.x; bufferX < max.x; bufferX += NUM_X_PIXELS_TO_SIMD)
{
#if UNROLL_LOOP
// Rasterise buffer(X, Y) pixel
{
__m128 checkArea = signedArea1;
@ -952,8 +982,17 @@ FILE_SCOPE void SIMDTexturedTriangle(DTRRenderBuffer *const renderBuffer, const
}
signedArea2 = _mm_add_ps(signedArea2, signedAreaPixelDeltaX);
}
#else
SIMDRasteriseTrianglePixel(renderBuffer, texture, bufferX, bufferY, max.x, uv1, uv2SubUv1,
uv3SubUv1, simdColor, triangleZ, signedArea1,
invSignedAreaParallelogram_4x);
SIMDRasteriseTrianglePixel(renderBuffer, texture, bufferX + 1, bufferY, max.x, uv1, uv2SubUv1,
uv3SubUv1, simdColor, triangleZ, signedArea2,
invSignedAreaParallelogram_4x);
signedArea1 = _mm_add_ps(signedArea1, signedAreaPixelDeltaX);
signedArea2 = _mm_add_ps(signedArea2, signedAreaPixelDeltaX);
#endif
}
signedAreaPixel1 = _mm_add_ps(signedAreaPixel1, signedAreaPixelDeltaY);
signedAreaPixel2 = _mm_add_ps(signedAreaPixel2, signedAreaPixelDeltaY);
}
@ -961,10 +1000,10 @@ FILE_SCOPE void SIMDTexturedTriangle(DTRRenderBuffer *const renderBuffer, const
DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle);
}
FILE_SCOPE void SlowTexturedTriangle(DTRRenderBuffer *const renderBuffer, const DqnV3 p1,
const DqnV3 p2, const DqnV3 p3, const DqnV2 uv1,
const DqnV2 uv2, const DqnV2 uv3, DTRBitmap *const texture,
DqnV4 color, const DqnV2i min, const DqnV2i max)
FILE_SCOPE void SlowTriangle(DTRRenderBuffer * const renderBuffer, const DqnV3 p1, const DqnV3 p2,
const DqnV3 p3, const DqnV2 uv1, const DqnV2 uv2, const DqnV2 uv3,
DTRBitmap *const texture, DqnV4 color, const DqnV2i min,
const DqnV2i max)
{
DTR_DEBUG_EP_TIMED_FUNCTION();
@ -1105,6 +1144,60 @@ FILE_SCOPE void SlowTexturedTriangle(DTRRenderBuffer *const renderBuffer, const
DEBUG_SLOW_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle);
}
DqnMat4 DqnMat4_GLViewport(f32 x, f32 y, f32 width, f32 height)
{
// Given a normalised coordinate from [-1, 1] for X, Y and Z, we want to map this
// back to viewport coordinates, or i.e. screen coordinates.
// Consider [-1, 1] for X. We want to (1.0f + [-1, 1] == [0, 2]). Then halve
// it, (0.5f * [0, 2] == [0, 1]) Then shift it to the viewport origin, (x,
// y). Then multiply this matrix by the normalised coordinate to find the
// screen space. This information is what this matrix stores.
DqnMat4 result = DqnMat4_Identity();
f32 halfWidth = width * 0.5f;
f32 halfHeight = height * 0.5f;
const f32 DEPTH_BUFFER_GRANULARITY = 255.0f;
const f32 HALF_DEPTH_BUFFER_GRANULARITY = DEPTH_BUFFER_GRANULARITY * 0.5f;
result.e[0][0] = halfWidth;
result.e[1][1] = halfHeight;
result.e[2][2] = HALF_DEPTH_BUFFER_GRANULARITY;
result.e[3][0] = x + halfWidth;
result.e[3][1] = y + halfHeight;
result.e[3][2] = HALF_DEPTH_BUFFER_GRANULARITY;
return result;
}
DqnMat4 DqnMat4_LookAt(DqnV3 eye, DqnV3 center, DqnV3 up)
{
DqnMat4 result = {0};
DqnV3 f = DqnV3_Normalise(DqnV3_Sub(eye, center));
DqnV3 s = DqnV3_Normalise(DqnV3_Cross(up, f));
DqnV3 u = DqnV3_Cross(f, s);
result.e[0][0] = s.x;
result.e[0][1] = u.x;
result.e[0][2] = f.x;
result.e[1][0] = s.y;
result.e[1][1] = u.y;
result.e[1][2] = f.y;
result.e[2][0] = s.z;
result.e[2][1] = u.z;
result.e[2][2] = f.z;
result.e[3][0] = DqnV3_Dot(s, eye);
result.e[3][1] = DqnV3_Dot(u, eye);
result.e[3][2] = -DqnV3_Dot(f, eye);
result.e[3][3] = 1.0f;
return result;
}
void DTRRender_TexturedTriangle(DTRRenderBuffer *const renderBuffer, DqnV3 p1, DqnV3 p2, DqnV3 p3,
DqnV2 uv1, DqnV2 uv2, DqnV2 uv3, DTRBitmap *const texture,
DqnV4 color, const DTRRenderTransform transform)
@ -1114,6 +1207,7 @@ void DTRRender_TexturedTriangle(DTRRenderBuffer *const renderBuffer, DqnV3 p1, D
////////////////////////////////////////////////////////////////////////////
Make3PointsClockwise(&p1, &p2, &p3);
#if 0
// TODO(doyle): Transform is only in 2d right now
DqnV2 origin = Get2DOriginFromTransformAnchor(p1.xy, p2.xy, p3.xy, transform);
DqnV2 pList[] = {p1.xy - origin, p2.xy - origin, p3.xy - origin};
@ -1122,6 +1216,9 @@ void DTRRender_TexturedTriangle(DTRRenderBuffer *const renderBuffer, DqnV3 p1, D
p1.xy = pList[0];
p2.xy = pList[1];
p3.xy = pList[2];
#else
DqnV2 pList[] = {p1.xy, p2.xy, p3.xy};
#endif
DqnRect bounds = GetBoundingBox(pList, DQN_ARRAY_COUNT(pList));
DqnRect screenSpace = DqnRect_4i(0, 0, renderBuffer->width - 1, renderBuffer->height - 1);
@ -1134,11 +1231,12 @@ void DTRRender_TexturedTriangle(DTRRenderBuffer *const renderBuffer, DqnV3 p1, D
////////////////////////////////////////////////////////////////////////////
if (globalDTRPlatformFlags.canUseSSE2 && 1)
{
SIMDTexturedTriangle(renderBuffer, p1, p2, p3, uv1, uv2, uv3, texture, color, min, max);
SIMDTriangle(renderBuffer, p1, p2, p3, uv1, uv2, uv3, texture, color, min, max);
}
else
{
SlowTexturedTriangle(renderBuffer, p1, p2, p3, uv1, uv2, uv3, texture, color, min, max);
SlowTriangle(renderBuffer, p1, p2, p3, uv1, uv2, uv3, texture, color, min,
max);
}
////////////////////////////////////////////////////////////////////////////
@ -1155,11 +1253,62 @@ void DTRRender_TexturedTriangle(DTRRenderBuffer *const renderBuffer, DqnV3 p1, D
}
}
#define HANDMADE_MATH_IMPLEMENTATION
#define HANDMADE_MATH_CPP_MODE
#include "external/tests/HandmadeMath.h"
FILE_SCOPE void Test()
{
f32 aspectRatio = 1;
DqnMat4 dqnPerspective = DqnMat4_Perspective(90, aspectRatio, 100, 1000);
hmm_mat4 hmmPerspective = HMM_Perspective (90, aspectRatio, 100, 1000);
{
hmm_vec3 hmmVec = HMM_Vec3i(1, 2, 3);
DqnV3 dqnVec = DqnV3_3i(1, 2, 3);
DqnMat4 dqnTranslate = DqnMat4_Translate(dqnVec.x, dqnVec.y, dqnVec.z);
hmm_mat4 hmmTranslate = HMM_Translate(hmmVec);
dqnVec *= 2;
hmmVec *= 2;
DqnMat4 dqnScale = DqnMat4_Scale(dqnVec.x, dqnVec.y, dqnVec.z);
hmm_mat4 hmmScale = HMM_Scale(hmmVec);
DqnMat4 dqnTsMatrix = DqnMat4_Mul(dqnTranslate, dqnScale);
hmm_mat4 hmmTsMatrix = HMM_MultiplyMat4(hmmTranslate, hmmScale);
for (i32 i = 0; i < 16; i++)
{
f32 *hmmTsMatrixf = (f32 *)&hmmTsMatrix;
f32 *dqnTsMatrixf = (f32 *)&dqnTsMatrix;
DQN_ASSERT(hmmTsMatrixf[i] == dqnTsMatrixf[i]);
}
int breakHere = 5;
}
{
DqnMat4 dqnViewMatrix = DqnMat4_LookAt(DqnV3_3f(4, 3, 3), DqnV3_1f(0), DqnV3_3f(0, 1, 0));
hmm_mat4 hmmViewMatrix =
HMM_LookAt(HMM_Vec3(4, 3, 3), HMM_Vec3(0, 0, 0), HMM_Vec3(0, 1, 0));
for (i32 i = 0; i < 16; i++)
{
f32 *hmmViewMatrixf = (f32 *)&hmmViewMatrix;
f32 *dqnViewMatrixf = (f32 *)&dqnViewMatrix;
DQN_ASSERT(hmmViewMatrixf[i] == dqnViewMatrixf[i]);
}
}
}
void DTRRender_Mesh(DTRRenderBuffer *const renderBuffer, DTRMesh *const mesh, const DqnV3 pos,
const f32 scale, const DqnV3 lightVector)
const f32 scale, const DqnV3 lightVector, const f32 dt)
{
if (!mesh) return;
Test();
for (u32 i = 0; i < mesh->numFaces; i++)
{
DTRMeshFace face = mesh->faces[i];
@ -1171,36 +1320,71 @@ void DTRRender_Mesh(DTRRenderBuffer *const renderBuffer, DTRMesh *const mesh, co
DqnV4 vertA = mesh->vertexes[vertAIndex];
DqnV4 vertB = mesh->vertexes[vertBIndex];
DqnV4 vertC = mesh->vertexes[vertCIndex];
DQN_ASSERT(vertA.w == 1);
DQN_ASSERT(vertB.w == 1);
DQN_ASSERT(vertC.w == 1);
// TODO(doyle): Some models have -ve indexes to refer to relative
// vertices. We should resolve that to positive indexes at run time.
DQN_ASSERT(vertAIndex < (i32)mesh->numVertexes);
DQN_ASSERT(vertBIndex < (i32)mesh->numVertexes);
DQN_ASSERT(vertCIndex < (i32)mesh->numVertexes);
// TODO(doyle): Use normals from model
DqnV4 vertAB = vertB - vertA;
DqnV4 vertAC = vertC - vertA;
DqnV3 normal = DqnV3_Cross(vertAC.xyz, vertAB.xyz);
f32 intensity = DqnV3_Dot(DqnV3_Normalise(normal), lightVector);
if (intensity < 0) continue;
DqnV4 modelCol = DqnV4_4f(1, 1, 1, 1);
modelCol.rgb *= DQN_ABS(intensity);
DqnV3 screenVA = (vertA.xyz * scale) + pos;
DqnV3 screenVB = (vertB.xyz * scale) + pos;
DqnV3 screenVC = (vertC.xyz * scale) + pos;
// Apply vertex shader, model view projection
DqnMat4 modelMatrix = {};
{
LOCAL_PERSIST f32 rotateDegrees = 0;
rotateDegrees += (dt * 0.0025f);
DqnMat4 translateMatrix = DqnMat4_Translate(pos.x, pos.y, pos.z);
DqnMat4 scaleMatrix = DqnMat4_Scale(scale, scale, scale);
DqnMat4 rotateMatrix = DqnMat4_Rotate(DQN_DEGREES_TO_RADIANS(rotateDegrees), 0.0f, 1.0f, 0.0f);
modelMatrix = DqnMat4_Mul(translateMatrix, DqnMat4_Mul(rotateMatrix, scaleMatrix));
}
// TODO(doyle): Why do we need rounding here? Maybe it's because
// I don't do any interpolation in the triangle routine for jagged
// edges.
#if 1
screenVA.x = (f32)(i32)(screenVA.x + 0.5f);
screenVA.y = (f32)(i32)(screenVA.y + 0.5f);
screenVB.x = (f32)(i32)(screenVB.x + 0.5f);
screenVB.y = (f32)(i32)(screenVB.y + 0.5f);
screenVC.x = (f32)(i32)(screenVC.x + 0.5f);
screenVC.y = (f32)(i32)(screenVC.y + 0.5f);
#endif
DqnV3 eye = DqnV3_3f(0, 0, 1);
DqnV3 up = DqnV3_3f(0, 1, 0);
DqnV3 center = DqnV3_3f(0, 0, 0);
DqnMat4 viewMatrix = DqnMat4_LookAt(eye, center, up);
f32 aspectRatio = (f32)renderBuffer->width / (f32)renderBuffer->height;
DqnMat4 perspective = DqnMat4_Perspective(80.0f, aspectRatio, 0.5f, 100.0f);
perspective = DqnMat4_Identity();
perspective.e[2][3] = -1.0f / DqnV3_Length(eye, center);
DqnMat4 viewport = DqnMat4_GLViewport(0, 0, (f32)renderBuffer->width, (f32)renderBuffer->height);
DqnMat4 modelView = DqnMat4_Mul(viewMatrix, modelMatrix);
DqnMat4 modelViewProjection = DqnMat4_Mul(perspective, modelView);
DqnMat4 viewPModelViewProjection = DqnMat4_Mul(viewport, modelViewProjection);
vertA = DqnMat4_MulV4(viewPModelViewProjection, vertA);
vertB = DqnMat4_MulV4(viewPModelViewProjection, vertB);
vertC = DqnMat4_MulV4(viewPModelViewProjection, vertC);
// Perspective Divide to Normalise Device Coordinates
vertA.xyz = (vertA.xyz / vertA.w);
vertB.xyz = (vertB.xyz / vertB.w);
vertC.xyz = (vertC.xyz / vertC.w);
// NOTE: Because we need to draw on pixel boundaries. We need to round
// up to closest pixel otherwise we will have gaps.
vertA.x = (f32)(i32)(vertA.x + 0.5f);
vertA.y = (f32)(i32)(vertA.y + 0.5f);
vertB.x = (f32)(i32)(vertB.x + 0.5f);
vertB.y = (f32)(i32)(vertB.y + 0.5f);
vertC.x = (f32)(i32)(vertC.x + 0.5f);
vertC.y = (f32)(i32)(vertC.y + 0.5f);
i32 textureAIndex = face.texIndex[0];
i32 textureBIndex = face.texIndex[1];
@ -1216,11 +1400,11 @@ void DTRRender_Mesh(DTRRenderBuffer *const renderBuffer, DTRMesh *const mesh, co
bool DEBUG_SIMPLE_MODE = false;
if (DTR_DEBUG && DEBUG_SIMPLE_MODE)
{
DTRRender_Triangle(renderBuffer, screenVA, screenVB, screenVC, modelCol);
DTRRender_Triangle(renderBuffer, vertA.xyz, vertB.xyz, vertC.xyz, modelCol);
}
else
{
DTRRender_TexturedTriangle(renderBuffer, screenVA, screenVB, screenVC, texA, texB,
DTRRender_TexturedTriangle(renderBuffer, vertA.xyz, vertB.xyz, vertC.xyz, texA, texB,
texC, &mesh->tex, modelCol);
}
@ -1228,11 +1412,11 @@ void DTRRender_Mesh(DTRRenderBuffer *const renderBuffer, DTRMesh *const mesh, co
if (DTR_DEBUG && DEBUG_WIREFRAME)
{
DqnV4 wireColor = DqnV4_4f(1.0f, 1.0f, 1.0f, 0.01f);
DTRRender_Line(renderBuffer, DqnV2i_V2(screenVA.xy), DqnV2i_V2(screenVB.xy),
DTRRender_Line(renderBuffer, DqnV2i_V2(vertA.xy), DqnV2i_V2(vertB.xy),
wireColor);
DTRRender_Line(renderBuffer, DqnV2i_V2(screenVB.xy), DqnV2i_V2(screenVC.xy),
DTRRender_Line(renderBuffer, DqnV2i_V2(vertB.xy), DqnV2i_V2(vertC.xy),
wireColor);
DTRRender_Line(renderBuffer, DqnV2i_V2(screenVC.xy), DqnV2i_V2(screenVA.xy),
DTRRender_Line(renderBuffer, DqnV2i_V2(vertC.xy), DqnV2i_V2(vertA.xy),
wireColor);
}
}

View File

@ -39,7 +39,7 @@ REM wd4100 unused argument parameters
REM wd4201 nonstandard extension used: nameless struct/union
REM wd4189 local variable is initialised but not referenced
REM wd4505 unreferenced local function not used will be removed
set CompileFlags=-EHsc -GR- -Oi -MT -Z7 -W4 -wd4100 -wd4201 -wd4189 -wd4505 -O2 -FAsc /I..\src\external\
set CompileFlags=-EHsc -GR- -Oi -MT -Z7 -W4 -wd4100 -wd4201 -wd4189 -wd4505 -Od -FAsc /I..\src\external\
set DLLFlags=/Fm%ProjectName% /Fo%ProjectName% /Fa%ProjectName% /Fe%ProjectName%
set Win32Flags=/FmWin32DTRenderer /FeWin32DTRenderer

View File

@ -529,8 +529,9 @@ typedef union DqnV3i
} DqnV3i;
// DqnV3
DQN_FILE_SCOPE DqnV3 DqnV3_3i(i32 x, i32 y, i32 z); // Create a vector using ints and typecast to floats
DQN_FILE_SCOPE DqnV3 DqnV3_1f(f32 xyz);
DQN_FILE_SCOPE DqnV3 DqnV3_3f(f32 x, f32 y, f32 z);
DQN_FILE_SCOPE DqnV3 DqnV3_3i(i32 x, i32 y, i32 z); // Create a vector using ints and typecast to floats
DQN_FILE_SCOPE DqnV3 DqnV3_Add (DqnV3 a, DqnV3 b);
DQN_FILE_SCOPE DqnV3 DqnV3_Sub (DqnV3 a, DqnV3 b);
@ -542,12 +543,16 @@ DQN_FILE_SCOPE bool DqnV3_Equals (DqnV3 a, DqnV3 b);
DQN_FILE_SCOPE DqnV3 DqnV3_Cross (DqnV3 a, DqnV3 b);
DQN_FILE_SCOPE DqnV3 DqnV3_Normalise (DqnV3 a);
DQN_FILE_SCOPE f32 DqnV3_Length (DqnV3 a, DqnV3 b);
DQN_FILE_SCOPE f32 DqnV3_LengthSquared(DqnV3 a, DqnV3 b);
DQN_FILE_SCOPE inline DqnV3 operator- (DqnV3 a, DqnV3 b) { return DqnV3_Sub (a, b); }
DQN_FILE_SCOPE inline DqnV3 operator+ (DqnV3 a, DqnV3 b) { return DqnV3_Add (a, b); }
DQN_FILE_SCOPE inline DqnV3 operator+ (DqnV3 a, f32 b) { return DqnV3_Add (a, DqnV3_1f(b)); }
DQN_FILE_SCOPE inline DqnV3 operator* (DqnV3 a, DqnV3 b) { return DqnV3_Hadamard(a, b); }
DQN_FILE_SCOPE inline DqnV3 operator* (DqnV3 a, f32 b) { return DqnV3_Scalef (a, b); }
DQN_FILE_SCOPE inline DqnV3 operator* (DqnV3 a, i32 b) { return DqnV3_Scalei (a, b); }
DQN_FILE_SCOPE inline DqnV3 operator/ (DqnV3 a, f32 b) { return DqnV3_Scalef (a, (1.0f/b)); }
DQN_FILE_SCOPE inline DqnV3 &operator*=(DqnV3 &a, DqnV3 b) { return (a = DqnV3_Hadamard(a, b)); }
DQN_FILE_SCOPE inline DqnV3 &operator*=(DqnV3 &a, f32 b) { return (a = DqnV3_Scalef (a, b)); }
DQN_FILE_SCOPE inline DqnV3 &operator*=(DqnV3 &a, i32 b) { return (a = DqnV3_Scalei (a, b)); }
@ -580,6 +585,7 @@ typedef union DqnV4 {
// Create a vector using ints and typecast to floats
DQN_FILE_SCOPE DqnV4 DqnV4_4i(i32 x, i32 y, i32 z, f32 w);
DQN_FILE_SCOPE DqnV4 DqnV4_4f(f32 x, f32 y, f32 z, f32 w);
DQN_FILE_SCOPE DqnV4 DqnV4_V3(DqnV3 a, f32 w);
DQN_FILE_SCOPE DqnV4 DqnV4_1f(f32 xyzw);
DQN_FILE_SCOPE DqnV4 DqnV4_Add (DqnV4 a, DqnV4 b);
@ -608,13 +614,15 @@ DQN_FILE_SCOPE inline bool operator==(DqnV4 a, DqnV4 b) { return DqnV4_E
////////////////////////////////////////////////////////////////////////////////
typedef union DqnMat4
{
// TODO(doyle): Row/column instead? More cache friendly since multiplication
// prefers rows.
DqnV4 col[4];
// Column/row
f32 e[4][4];
f32 e[4][4]; // Column/row
} DqnMat4;
DQN_FILE_SCOPE DqnMat4 DqnMat4_Identity ();
DQN_FILE_SCOPE DqnMat4 DqnMat4_Ortho (f32 left, f32 right, f32 bottom, f32 top, f32 zNear, f32 zFar);
DQN_FILE_SCOPE DqnMat4 DqnMat4_Orthographic(f32 left, f32 right, f32 bottom, f32 top, f32 zNear, f32 zFar);
DQN_FILE_SCOPE DqnMat4 DqnMat4_Perspective (f32 fovYDegrees, f32 aspectRatio, f32 zNear, f32 zFar);
DQN_FILE_SCOPE DqnMat4 DqnMat4_Translate (f32 x, f32 y, f32 z);
DQN_FILE_SCOPE DqnMat4 DqnMat4_Rotate (f32 radians, f32 x, f32 y, f32 z);
DQN_FILE_SCOPE DqnMat4 DqnMat4_Scale (f32 x, f32 y, f32 z);
@ -2065,18 +2073,21 @@ DQN_FILE_SCOPE bool DqnV2i_Equals(DqnV2i a, DqnV2i b)
////////////////////////////////////////////////////////////////////////////////
// Vec3
////////////////////////////////////////////////////////////////////////////////
DQN_FILE_SCOPE DqnV3 DqnV3_1f(f32 xyz)
{
DqnV3 result = {xyz, xyz, xyz};
return result;
}
DQN_FILE_SCOPE DqnV3 DqnV3_3f(f32 x, f32 y, f32 z)
{
DqnV3 result = {};
result.x = x;
result.y = y;
result.z = z;
DqnV3 result = {x, y, z};
return result;
}
DQN_FILE_SCOPE DqnV3 DqnV3_3i(i32 x, i32 y, i32 z)
{
DqnV3 result = DqnV3_3f((f32)x, (f32)y, (f32)z);
DqnV3 result = {(f32)x, (f32)y, (f32)z};
return result;
}
@ -2174,6 +2185,24 @@ DQN_FILE_SCOPE DqnV3 DqnV3_Normalise(DqnV3 a)
return result;
}
DQN_FILE_SCOPE f32 DqnV3_LengthSquared(DqnV3 a, DqnV3 b)
{
f32 x = b.x - a.x;
f32 y = b.y - a.y;
f32 z = b.z - a.z;
f32 result = (DQN_SQUARED(x) + DQN_SQUARED(y) + DQN_SQUARED(z));
return result;
}
DQN_FILE_SCOPE f32 DqnV3_Length(DqnV3 a, DqnV3 b)
{
f32 lengthSq = DqnV3_LengthSquared(a, b);
if (lengthSq == 0) return 0;
f32 result = DqnMath_Sqrtf(lengthSq);
return result;
}
DQN_FILE_SCOPE DqnV3i DqnV3i_3i(i32 x, i32 y, i32 z)
{
DqnV3i result = {x, y, z};
@ -2201,6 +2230,14 @@ DQN_FILE_SCOPE DqnV4 DqnV4_4i(i32 x, i32 y, i32 z, i32 w)
return result;
}
DQN_FILE_SCOPE DqnV4 DqnV4_V3(DqnV3 a, f32 w)
{
DqnV4 result;
result.xyz = a;
result.w = w;
return result;
}
DQN_FILE_SCOPE DqnV4 DqnV4_1f(f32 xyzw)
{
DqnV4 result = {xyzw, xyzw, xyzw, xyzw};
@ -2289,8 +2326,8 @@ DQN_FILE_SCOPE DqnMat4 DqnMat4_Identity()
return result;
}
DQN_FILE_SCOPE DqnMat4
DqnMat4_Ortho(f32 left, f32 right, f32 bottom, f32 top, f32 zNear, f32 zFar)
DQN_FILE_SCOPE DqnMat4 DqnMat4_Orthographic(f32 left, f32 right, f32 bottom, f32 top, f32 zNear,
f32 zFar)
{
DqnMat4 result = DqnMat4_Identity();
result.e[0][0] = +2.0f / (right - left);
@ -2304,6 +2341,24 @@ DqnMat4_Ortho(f32 left, f32 right, f32 bottom, f32 top, f32 zNear, f32 zFar)
return result;
}
DQN_FILE_SCOPE DqnMat4 DqnMat4_Perspective(f32 fovYDegrees, f32 aspectRatio, f32 zNear, f32 zFar)
{
f32 fovYRadians = DQN_DEGREES_TO_RADIANS(fovYDegrees);
f32 fovYRadiansOver2 = fovYRadians * 0.5f;
f32 tanFovYRadiansOver2 = tanf(fovYRadiansOver2);
f32 zNearSubZFar = zNear - zFar;
DqnMat4 result = DqnMat4_Identity();
result.e[0][0] = 1.0f / (tanFovYRadiansOver2 * aspectRatio);
result.e[1][1] = 1.0f / tanFovYRadiansOver2;
result.e[2][2] = (zNear + zFar) / zNearSubZFar;
result.e[2][3] = -1.0f;
result.e[3][2] = (2.0f * zNear * zFar) / zNearSubZFar;
result.e[3][3] = 0.0f;
return result;
}
DQN_FILE_SCOPE DqnMat4 DqnMat4_Translate(f32 x, f32 y, f32 z)
{
DqnMat4 result = DqnMat4_Identity();
@ -2350,7 +2405,8 @@ DQN_FILE_SCOPE DqnMat4 DqnMat4_Mul(DqnMat4 a, DqnMat4 b)
{
DqnMat4 result = {0};
for (i32 j = 0; j < 4; j++) {
for (i32 i = 0; i < 4; i++) {
for (i32 i = 0; i < 4; i++)
{
result.e[j][i] = a.e[0][i] * b.e[j][0]
+ a.e[1][i] * b.e[j][1]
+ a.e[2][i] * b.e[j][2]
@ -2364,15 +2420,10 @@ DQN_FILE_SCOPE DqnMat4 DqnMat4_Mul(DqnMat4 a, DqnMat4 b)
DQN_FILE_SCOPE DqnV4 DqnMat4_MulV4(DqnMat4 a, DqnV4 b)
{
DqnV4 result = {0};
result.x = (a.e[0][0] * b.x) + (a.e[1][0] * b.y) + (a.e[2][0] * b.z) +
(a.e[3][0] * b.w);
result.y = (a.e[0][1] * b.x) + (a.e[1][1] * b.y) + (a.e[2][1] * b.z) +
(a.e[3][1] * b.w);
result.z = (a.e[0][2] * b.x) + (a.e[1][2] * b.y) + (a.e[2][2] * b.z) +
(a.e[3][2] * b.w);
result.w = (a.e[0][3] * b.x) + (a.e[1][3] * b.y) + (a.e[2][3] * b.z) +
(a.e[3][3] * b.w);
result.x = (a.e[0][0] * b.x) + (a.e[1][0] * b.y) + (a.e[2][0] * b.z) + (a.e[3][0] * b.w);
result.y = (a.e[0][1] * b.x) + (a.e[1][1] * b.y) + (a.e[2][1] * b.z) + (a.e[3][1] * b.w);
result.z = (a.e[0][2] * b.x) + (a.e[1][2] * b.y) + (a.e[2][2] * b.z) + (a.e[3][2] * b.w);
result.w = (a.e[0][3] * b.x) + (a.e[1][3] * b.y) + (a.e[2][3] * b.z) + (a.e[3][3] * b.w);
return result;
}

View File

@ -0,0 +1,116 @@
#include <vector>
#include <cassert>
#include <cmath>
#include <iostream>
#include "geometry.h"
template <> Vec3<float>::Vec3(Matrix m) : x(m[0][0]/m[3][0]), y(m[1][0]/m[3][0]), z(m[2][0]/m[3][0]) {}
template <> template <> Vec3<int>::Vec3(const Vec3<float> &v) : x(int(v.x+.5)), y(int(v.y+.5)), z(int(v.z+.5)) {}
template <> template <> Vec3<float>::Vec3(const Vec3<int> &v) : x(v.x), y(v.y), z(v.z) {}
Matrix::Matrix(Vec3f v) : m(std::vector<std::vector<float> >(4, std::vector<float>(1, 1.f))), rows(4), cols(1) {
m[0][0] = v.x;
m[1][0] = v.y;
m[2][0] = v.z;
}
Matrix::Matrix(int r, int c) : m(std::vector<std::vector<float> >(r, std::vector<float>(c, 0.f))), rows(r), cols(c) { }
int Matrix::nrows() {
return rows;
}
int Matrix::ncols() {
return cols;
}
Matrix Matrix::identity(int dimensions) {
Matrix E(dimensions, dimensions);
for (int i=0; i<dimensions; i++) {
for (int j=0; j<dimensions; j++) {
E[i][j] = (i==j ? 1.f : 0.f);
}
}
return E;
}
std::vector<float>& Matrix::operator[](const int i) {
assert(i>=0 && i<rows);
return m[i];
}
Matrix Matrix::operator*(const Matrix& a) {
assert(cols == a.rows);
Matrix result(rows, a.cols);
for (int i=0; i<rows; i++) {
for (int j=0; j<a.cols; j++) {
result.m[i][j] = 0.f;
for (int k=0; k<cols; k++) {
result.m[i][j] += m[i][k]*a.m[k][j];
}
}
}
return result;
}
Matrix Matrix::transpose() {
Matrix result(cols, rows);
for(int i=0; i<rows; i++)
for(int j=0; j<cols; j++)
result[j][i] = m[i][j];
return result;
}
Matrix Matrix::inverse() {
assert(rows==cols);
// augmenting the square matrix with the identity matrix of the same dimensions a => [ai]
Matrix result(rows, cols*2);
for(int i=0; i<rows; i++)
for(int j=0; j<cols; j++)
result[i][j] = m[i][j];
for(int i=0; i<rows; i++)
result[i][i+cols] = 1;
// first pass
for (int i=0; i<rows-1; i++) {
// normalize the first row
for(int j=result.cols-1; j>=0; j--)
result[i][j] /= result[i][i];
for (int k=i+1; k<rows; k++) {
float coeff = result[k][i];
for (int j=0; j<result.cols; j++) {
result[k][j] -= result[i][j]*coeff;
}
}
}
// normalize the last row
for(int j=result.cols-1; j>=rows-1; j--)
result[rows-1][j] /= result[rows-1][rows-1];
// second pass
for (int i=rows-1; i>0; i--) {
for (int k=i-1; k>=0; k--) {
float coeff = result[k][i];
for (int j=0; j<result.cols; j++) {
result[k][j] -= result[i][j]*coeff;
}
}
}
// cut the identity matrix back
Matrix truncate(rows, cols);
for(int i=0; i<rows; i++)
for(int j=0; j<cols; j++)
truncate[i][j] = result[i][j+cols];
return truncate;
}
std::ostream& operator<<(std::ostream& s, Matrix& m) {
for (int i=0; i<m.nrows(); i++) {
for (int j=0; j<m.ncols(); j++) {
s << m[i][j];
if (j<m.ncols()-1) s << "\t";
}
s << "\n";
}
return s;
}

View File

@ -1,221 +1,75 @@
#ifndef __GEOMETRY_H__
#define __GEOMETRY_H__
#include <cmath>
#include <vector>
#include <cassert>
#include <iostream>
template<size_t DimCols,size_t DimRows,typename T> class mat;
class Matrix;
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 <class t> struct Vec2 {
t x, y;
Vec2<t>() : x(t()), y(t()) {}
Vec2<t>(t _x, t _y) : x(_x), y(_y) {}
Vec2<t> operator +(const Vec2<t> &V) const { return Vec2<t>(x+V.x, y+V.y); }
Vec2<t> operator -(const Vec2<t> &V) const { return Vec2<t>(x-V.x, y-V.y); }
Vec2<t> operator *(float f) const { return Vec2<t>(x*f, y*f); }
t& operator[](const int i) { return i<=0 ? x : y; }
template <class > friend std::ostream& operator<<(std::ostream& s, Vec2<t>& v);
};
/////////////////////////////////////////////////////////////////////////////////
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 <class t> struct Vec3 {
t x, y, z;
Vec3<t>() : x(t()), y(t()), z(t()) { }
Vec3<t>(t _x, t _y, t _z) : x(_x), y(_y), z(_z) {}
Vec3<t>(Matrix m);
template <class u> Vec3<t>(const Vec3<u> &v);
Vec3<t> operator ^(const Vec3<t> &v) const { return Vec3<t>(y*v.z-z*v.y, z*v.x-x*v.z, x*v.y-y*v.x); }
Vec3<t> operator +(const Vec3<t> &v) const { return Vec3<t>(x+v.x, y+v.y, z+v.z); }
Vec3<t> operator -(const Vec3<t> &v) const { return Vec3<t>(x-v.x, y-v.y, z-v.z); }
Vec3<t> operator *(float f) const { return Vec3<t>(x*f, y*f, z*f); }
t operator *(const Vec3<t> &v) const { return x*v.x + y*v.y + z*v.z; }
float norm () const { return std::sqrt(x*x+y*y+z*z); }
Vec3<t> & normalize(t l=1) { *this = (*this)*(l/norm()); return *this; }
t& operator[](const int i) { return i<=0 ? x : (1==i ? y : z); }
template <class > friend std::ostream& operator<<(std::ostream& s, Vec3<t>& v);
};
/////////////////////////////////////////////////////////////////////////////////
typedef Vec2<float> Vec2f;
typedef Vec2<int> Vec2i;
typedef Vec3<float> Vec3f;
typedef Vec3<int> Vec3i;
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; }
template <> template <> Vec3<int>::Vec3(const Vec3<float> &v);
template <> template <> Vec3<float>::Vec3(const Vec3<int> &v);
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 <class t> std::ostream& operator<<(std::ostream& s, Vec2<t>& v) {
s << "(" << v.x << ", " << v.y << ")\n";
return s;
}
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 <class t> std::ostream& operator<<(std::ostream& s, Vec3<t>& v) {
s << "(" << v.x << ", " << v.y << ", " << v.z << ")\n";
return s;
}
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);
}
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 ;
}
/////////////////////////////////////////////////////////////////////////////////
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];
class Matrix {
std::vector<std::vector<float> > m;
int rows, cols;
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;
}
Matrix(int r=4, int c=4);
Matrix(Vec3f v);
int nrows();
int ncols();
static Matrix identity(int dimensions);
std::vector<float>& operator[](const int i);
Matrix operator*(const Matrix& a);
Matrix transpose();
Matrix inverse();
friend std::ostream& operator<<(std::ostream& s, Matrix& m);
};
/////////////////////////////////////////////////////////////////////////////////
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;
}
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;
}
/////////////////////////////////////////////////////////////////////////////////
typedef vec<2, float> Vec2f;
typedef vec<2, int> Vec2i;
typedef vec<3, float> Vec3f;
typedef vec<3, int> Vec3i;
typedef vec<4, float> Vec4f;
typedef mat<4,4,float> Matrix;
#endif //__GEOMETRY_H__

View File

@ -5,7 +5,7 @@
#include <vector>
#include "model.h"
Model::Model(const char *filename) : verts_(), faces_() {
Model::Model(const char *filename) : verts_(), faces_(), norms_(), uv_(), diffusemap_() {
std::ifstream in;
in.open (filename, std::ifstream::in);
if (in.fail()) return;
@ -19,18 +19,29 @@ Model::Model(const char *filename) : verts_(), faces_() {
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<int> f;
int itrash, idx;
std::vector<Vec3i> f;
Vec3i tmp;
iss >> trash;
while (iss >> idx >> trash >> itrash >> trash >> itrash) {
idx--; // in wavefront obj all indices start at 1, not zero
f.push_back(idx);
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() << std::endl;
std::cerr << "# v# " << verts_.size() << " f# " << faces_.size() << " vt# " << uv_.size() << " vn# " << norms_.size() << std::endl;
load_texture(filename, "_diffuse.tga", diffusemap_);
}
Model::~Model() {
@ -45,10 +56,37 @@ int Model::nfaces() {
}
std::vector<int> Model::face(int idx) {
return faces_[idx];
std::vector<int> face;
for (int i=0; i<(int)faces_[idx].size(); i++) face.push_back(faces_[idx][i][0]);
return face;
}
Vec3f Model::vert(int i) {
return verts_[i];
}
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(Vec2i uv) {
return diffusemap_.get(uv.x, uv.y);
}
Vec2i Model::uv(int iface, int nvert) {
int idx = faces_[iface][nvert][1];
return Vec2i(uv_[idx].x*diffusemap_.get_width(), uv_[idx].y*diffusemap_.get_height());
}
Vec3f Model::norm(int iface, int nvert) {
int idx = faces_[iface][nvert][2];
return norms_[idx].normalize();
}

View File

@ -3,17 +3,25 @@
#include <vector>
#include "geometry.h"
#include "tgaimage.h"
class Model {
private:
std::vector<Vec3f> verts_;
std::vector<std::vector<int> > faces_;
std::vector<std::vector<Vec3i> > faces_; // attention, this Vec3i means vertex/uv/normal
std::vector<Vec3f> norms_;
std::vector<Vec2f> uv_;
TGAImage diffusemap_;
void load_texture(std::string filename, const char *suffix, TGAImage &img);
public:
Model(const char *filename);
~Model();
int nverts();
int nfaces();
Vec3f norm(int iface, int nvert);
Vec3f vert(int i);
Vec2i uv(int iface, int nvert);
TGAColor diffuse(Vec2i uv);
std::vector<int> face(int idx);
};

View File

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

View File

@ -0,0 +1,98 @@
#ifndef __IMAGE_H__
#define __IMAGE_H__
#include <fstream>
#pragma pack(push,1)
struct TGA_Header {
char idlength;
char colormaptype;
char datatypecode;
short colormaporigin;
short colormaplength;
char colormapdepth;
short x_origin;
short y_origin;
short width;
short height;
char bitsperpixel;
char imagedescriptor;
};
#pragma pack(pop)
struct TGAColor {
unsigned char bgra[4];
unsigned char bytespp;
TGAColor() : bgra(), bytespp(1) {
for (int i=0; i<4; i++) bgra[i] = 0;
}
TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) {
bgra[0] = B;
bgra[1] = G;
bgra[2] = R;
bgra[3] = A;
}
TGAColor(unsigned char v) : bgra(), bytespp(1) {
for (int i=0; i<4; i++) bgra[i] = 0;
bgra[0] = v;
}
TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) {
for (int i=0; i<(int)bpp; i++) {
bgra[i] = p[i];
}
for (int i=bpp; i<4; i++) {
bgra[i] = 0;
}
}
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__

View File

@ -0,0 +1,215 @@
#pragma warning(push)
#pragma warning(disable : 4459; disable : 4244)
#include "geometry.cpp"
#include "model.cpp"
#include "tgaimage.cpp"
#include <cmath>
#include <limits>
#include <vector>
const int trWidth = 800;
const int trHeight = 800;
const int trDepth = 255;
Model *model = NULL;
int *zbuffer = NULL;
Vec3f light_dir = Vec3f(1, -1, 1).normalize();
Matrix viewport(int x, int y, int w, int h)
{
Matrix m = Matrix::identity(4);
#if 0
m[0][3] = x; // + w / 2.f;
m[1][3] = y; // + h / 2.f;
// m[2][3] = trDepth / 2.f;
m[0][0] = w / 2.f;
m[1][1] = h / 2.f;
// m[2][2] = trDepth / 2.f;
#else
m[0][3] = x + w / 2.f;
m[1][3] = y + h / 2.f;
m[2][3] = trDepth / 2.f;
m[0][0] = w / 2.f;
m[1][1] = h / 2.f;
m[2][2] = trDepth / 2.f;
#endif
return m;
}
Matrix lookat(Vec3f eye, Vec3f center, Vec3f up)
{
Vec3f z = (eye - center).normalize();
Vec3f x = (up ^ z).normalize();
Vec3f y = (z ^ x).normalize();
Matrix res = Matrix::identity(4);
res[0][0] = x[0];
res[0][1] = x[1];
res[0][2] = x[2];
res[1][0] = y[0];
res[1][1] = y[1];
res[1][2] = y[2];
res[2][0] = z[0];
res[2][1] = z[1];
res[2][2] = z[2];
res[0][3] = -center[0];
res[1][3] = -center[1];
res[2][3] = -center[2];
for (int i = 0; i < 3; i++)
{
res[0][i] = x[i];
res[1][i] = y[i];
res[2][i] = z[i];
res[i][3] = -center[i];
}
return res;
}
void triangle(Vec3i t0, Vec3i t1, Vec3i t2, float ity0, float ity1, float ity2, TGAImage &image,
int *zbuffer)
{
if (t0.y == t1.y && t0.y == t2.y) return; // i dont care about degenerate triangles
if (t0.y > t1.y)
{
std::swap(t0, t1);
std::swap(ity0, ity1);
}
if (t0.y > t2.y)
{
std::swap(t0, t2);
std::swap(ity0, ity2);
}
if (t1.y > t2.y)
{
std::swap(t1, t2);
std::swap(ity1, ity2);
}
int total_trHeight = t2.y - t0.y;
for (int i = 0; i < total_trHeight; i++)
{
bool second_half = i > t1.y - t0.y || t1.y == t0.y;
int segment_trHeight = second_half ? t2.y - t1.y : t1.y - t0.y;
float alpha = (float)i / total_trHeight;
float beta = (float)(i - (second_half ? t1.y - t0.y : 0)) /
segment_trHeight; // be careful: with above conditions no division by zero here
Vec3i A = t0 + Vec3f(t2 - t0) * alpha;
Vec3i B = second_half ? t1 + Vec3f(t2 - t1) * beta : t0 + Vec3f(t1 - t0) * beta;
float ityA = ity0 + (ity2 - ity0) * alpha;
float ityB = second_half ? ity1 + (ity2 - ity1) * beta : ity0 + (ity1 - ity0) * beta;
if (A.x > B.x)
{
std::swap(A, B);
std::swap(ityA, ityB);
}
for (int j = A.x; j <= B.x; j++)
{
float phi = B.x == A.x ? 1. : (float)(j - A.x) / (B.x - A.x);
Vec3i P = Vec3f(A) + Vec3f(B - A) * phi;
float ityP = ityA + (ityB - ityA) * phi;
int idx = P.x + P.y * trWidth;
if (P.x >= trWidth || P.y >= trHeight || P.x < 0 || P.y < 0) continue;
if (zbuffer[idx] < P.z)
{
zbuffer[idx] = P.z;
#if 1
image.set(P.x, P.y, TGAColor(255 * ity0, 255 * ity0, 255 * ity0));
#else
image.set(P.x, P.y, TGAColor(255, 255, 255) * ityP);
#endif
}
}
}
}
int tinyrenderer(int argc, char **argv)
{
if (2 == argc)
{
model = new Model(argv[1]);
}
else
{
model = new Model("african_head.obj");
}
zbuffer = new int[trWidth * trHeight];
for (int i = 0; i < trWidth * trHeight; i++)
{
zbuffer[i] = std::numeric_limits<int>::min();
}
Vec3f eye(1, 1, 3);
Vec3f center(0, 0, 0);
{ // draw the model
Matrix viewMatrix = lookat(eye, center, Vec3f(0, 1, 0));
Matrix perspective = Matrix::identity(4);
Matrix viewp = viewport(0, 0, trWidth, trHeight);
auto cam = -1.f / (eye - center).norm();
perspective[3][2] = cam;
std::cerr << viewMatrix << std::endl;
std::cerr << perspective << std::endl;
std::cerr << viewp << std::endl;
Matrix z = (viewp * perspective * viewMatrix);
std::cerr << z << std::endl;
TGAImage image(trWidth, trHeight, TGAImage::RGB);
for (int i = 0; i < model->nfaces(); i++)
{
std::vector<int> face = model->face(i);
Vec3f screen_coords[3];
Vec3f world_coords[3];
float intensity[3];
for (int j = 0; j < 3; j++)
{
#if 1
Vec3f v = model->vert(face[j]);
v = Vec3f(0.1347f, -0.1472f, 0.4880f);
Matrix perspectiveViewMatrix = perspective * viewMatrix;
Matrix viewpPerspectiveViewMatrix = viewp * perspectiveViewMatrix;
Vec3f screen = Vec3f(viewpPerspectiveViewMatrix * Matrix(v));
screen_coords[j] = screen;
world_coords[j] = v;
intensity[j] = model->norm(i, j) * light_dir;
#else
Vec3f v = model->vert(face[j]);
screen_coords[j] = Vec3f(viewp * perspective * viewMatrix * Matrix(v));
world_coords[j] = v;
intensity[j] = model->norm(i, j) * light_dir;
#endif
}
triangle(screen_coords[0], screen_coords[1], screen_coords[2], intensity[0],
intensity[1], intensity[2], image, zbuffer);
}
image.flip_vertically(); // i want to have the origin at the left bottom corner of the image
image.write_tga_file("output.tga");
}
{ // dump z-buffer (debugging purposes only)
TGAImage zbimage(trWidth, trHeight, TGAImage::GRAYSCALE);
for (int i = 0; i < trWidth; i++)
{
for (int j = 0; j < trHeight; j++)
{
zbimage.set(i, j, TGAColor(zbuffer[i + j * trWidth]));
}
}
zbimage
.flip_vertically(); // i want to have the origin at the left bottom corner of the image
zbimage.write_tga_file("zbuffer.tga");
}
delete model;
delete[] zbuffer;
return 0;
}
#pragma warning(pop)