diff --git a/src/DTRenderer.cpp b/src/DTRenderer.cpp index f5b3ea6..26e2429 100644 --- a/src/DTRenderer.cpp +++ b/src/DTRenderer.cpp @@ -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); } } diff --git a/src/DTRendererAsset.cpp b/src/DTRendererAsset.cpp index 7b7ece2..a2f36df 100644 --- a/src/DTRendererAsset.cpp +++ b/src/DTRendererAsset.cpp @@ -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,10 +349,10 @@ 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); + WavefModelFace face = WavefModelFaceInit(3, memAPI); i32 numVertexesParsed = 0; bool moreVertexesToParse = true; while (moreVertexesToParse) @@ -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. diff --git a/src/DTRendererDebug.cpp b/src/DTRendererDebug.cpp index f161b0f..318c0cf 100644 --- a/src/DTRendererDebug.cpp +++ b/src/DTRendererDebug.cpp @@ -6,9 +6,9 @@ #include "dqn.h" -#include "external/tests/tinyrenderer/geometry.h" -#include "external/tests/tinyrenderer/model.cpp" -#include +#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 correctFace = model.face(i); + std::vector 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) diff --git a/src/DTRendererDebug.h b/src/DTRendererDebug.h index d87951c..15d6de6 100644 --- a/src/DTRendererDebug.h +++ b/src/DTRendererDebug.h @@ -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); diff --git a/src/DTRendererRender.cpp b/src/DTRendererRender.cpp index 3e66395..2c45e08 100644 --- a/src/DTRendererRender.cpp +++ b/src/DTRendererRender.cpp @@ -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); @@ -865,13 +890,18 @@ FILE_SCOPE void SIMDTexturedTriangle(DTRRenderBuffer *const renderBuffer, const DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle_Preamble_SIMDStep); } - 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; + const DqnV2 uv2SubUv1 = uv2 - uv1; + const DqnV2 uv3SubUv1 = uv3 - uv1; + +#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); } } diff --git a/src/build.bat b/src/build.bat index 9b76365..e7cd18c 100644 --- a/src/build.bat +++ b/src/build.bat @@ -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 diff --git a/src/dqn.h b/src/dqn.h index 07719e6..545330a 100644 --- a/src/dqn.h +++ b/src/dqn.h @@ -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); @@ -541,19 +542,23 @@ DQN_FILE_SCOPE f32 DqnV3_Dot (DqnV3 a, DqnV3 b); 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 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, 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, 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)); } -DQN_FILE_SCOPE inline DqnV3 &operator-=(DqnV3 &a, DqnV3 b) { return (a = DqnV3_Sub (a, b)); } -DQN_FILE_SCOPE inline DqnV3 &operator+=(DqnV3 &a, DqnV3 b) { return (a = DqnV3_Add (a, b)); } -DQN_FILE_SCOPE inline bool operator==(DqnV3 a, DqnV3 b) { return DqnV3_Equals (a, 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)); } +DQN_FILE_SCOPE inline DqnV3 &operator-=(DqnV3 &a, DqnV3 b) { return (a = DqnV3_Sub (a, b)); } +DQN_FILE_SCOPE inline DqnV3 &operator+=(DqnV3 &a, DqnV3 b) { return (a = DqnV3_Add (a, b)); } +DQN_FILE_SCOPE inline bool operator==(DqnV3 a, DqnV3 b) { return DqnV3_Equals (a, b); } // DqnV3i DQN_FILE_SCOPE DqnV3i DqnV3i_3i(i32 x, i32 y, i32 z); @@ -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,18 +614,20 @@ 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_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); -DQN_FILE_SCOPE DqnMat4 DqnMat4_Mul (DqnMat4 a, DqnMat4 b); -DQN_FILE_SCOPE DqnV4 DqnMat4_MulV4 (DqnMat4 a, DqnV4 b); +DQN_FILE_SCOPE DqnMat4 DqnMat4_Identity (); +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); +DQN_FILE_SCOPE DqnMat4 DqnMat4_Mul (DqnMat4 a, DqnMat4 b); +DQN_FILE_SCOPE DqnV4 DqnMat4_MulV4 (DqnMat4 a, DqnV4 b); //////////////////////////////////////////////////////////////////////////////// // Other Math @@ -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}; @@ -2281,7 +2318,7 @@ DQN_FILE_SCOPE bool DqnV4_Equals(DqnV4 a, DqnV4 b) //////////////////////////////////////////////////////////////////////////////// DQN_FILE_SCOPE DqnMat4 DqnMat4_Identity() { - DqnMat4 result = {0}; + DqnMat4 result = {0}; result.e[0][0] = 1; result.e[1][1] = 1; result.e[2][2] = 1; @@ -2289,13 +2326,13 @@ 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); - result.e[1][1] = +2.0f / (top - bottom); - result.e[2][2] = -2.0f / (zFar - zNear); + result.e[0][0] = +2.0f / (right - left); + result.e[1][1] = +2.0f / (top - bottom); + result.e[2][2] = -2.0f / (zFar - zNear); result.e[3][0] = -(right + left) / (right - left); result.e[3][1] = -(top + bottom) / (top - bottom); @@ -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; } diff --git a/src/external/tests/tinyrenderer/geometry.cpp b/src/external/tests/tinyrenderer/geometry.cpp new file mode 100644 index 0000000..e020dcb --- /dev/null +++ b/src/external/tests/tinyrenderer/geometry.cpp @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include "geometry.h" + +template <> Vec3::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::Vec3(const Vec3 &v) : x(int(v.x+.5)), y(int(v.y+.5)), z(int(v.z+.5)) {} +template <> template <> Vec3::Vec3(const Vec3 &v) : x(v.x), y(v.y), z(v.z) {} + +Matrix::Matrix(Vec3f v) : m(std::vector >(4, std::vector(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 >(r, std::vector(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& Matrix::operator[](const int i) { + assert(i>=0 && i [ai] + Matrix result(rows, cols*2); + for(int i=0; i=0; j--) + result[i][j] /= result[i][i]; + for (int k=i+1; k=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 #include -#include -#include -template class mat; +class Matrix; -template struct vec { - vec() { for (size_t i=DIM; i--; data_[i] = T()); } - T& operator[](const size_t i) { assert(i struct Vec2 { + t x, y; + Vec2() : x(t()), y(t()) {} + Vec2(t _x, t _y) : x(_x), y(_y) {} + Vec2 operator +(const Vec2 &V) const { return Vec2(x+V.x, y+V.y); } + Vec2 operator -(const Vec2 &V) const { return Vec2(x-V.x, y-V.y); } + Vec2 operator *(float f) const { return Vec2(x*f, y*f); } + t& operator[](const int i) { return i<=0 ? x : y; } + template friend std::ostream& operator<<(std::ostream& s, Vec2& v); }; -///////////////////////////////////////////////////////////////////////////////// - -template struct vec<2,T> { - vec() : x(T()), y(T()) {} - vec(T X, T Y) : x(X), y(Y) {} - template vec<2,T>(const vec<2,U> &v); - T& operator[](const size_t i) { assert(i<2); return i<=0 ? x : y; } - const T& operator[](const size_t i) const { assert(i<2); return i<=0 ? x : y; } - - T x,y; +template struct Vec3 { + t x, y, z; + Vec3() : x(t()), y(t()), z(t()) { } + Vec3(t _x, t _y, t _z) : x(_x), y(_y), z(_z) {} + Vec3(Matrix m); + template Vec3(const Vec3 &v); + Vec3 operator ^(const Vec3 &v) const { return Vec3(y*v.z-z*v.y, z*v.x-x*v.z, x*v.y-y*v.x); } + Vec3 operator +(const Vec3 &v) const { return Vec3(x+v.x, y+v.y, z+v.z); } + Vec3 operator -(const Vec3 &v) const { return Vec3(x-v.x, y-v.y, z-v.z); } + Vec3 operator *(float f) const { return Vec3(x*f, y*f, z*f); } + t operator *(const Vec3 &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 & normalize(t l=1) { *this = (*this)*(l/norm()); return *this; } + t& operator[](const int i) { return i<=0 ? x : (1==i ? y : z); } + template friend std::ostream& operator<<(std::ostream& s, Vec3& v); }; -///////////////////////////////////////////////////////////////////////////////// +typedef Vec2 Vec2f; +typedef Vec2 Vec2i; +typedef Vec3 Vec3f; +typedef Vec3 Vec3i; -template struct vec<3,T> { - vec() : x(T()), y(T()), z(T()) {} - vec(T X, T Y, T Z) : x(X), y(Y), z(Z) {} - template vec<3,T>(const vec<3,U> &v); - T& operator[](const size_t i) { assert(i<3); return i<=0 ? x : (1==i ? y : z); } - const T& operator[](const size_t i) const { assert(i<3); return i<=0 ? x : (1==i ? y : z); } - float norm() { return std::sqrt(x*x+y*y+z*z); } - vec<3,T> & normalize(T l=1) { *this = (*this)*(l/norm()); return *this; } +template <> template <> Vec3::Vec3(const Vec3 &v); +template <> template <> Vec3::Vec3(const Vec3 &v); - T x,y,z; -}; -///////////////////////////////////////////////////////////////////////////////// - -template T operator*(const vec& lhs, const vec& rhs) { - T ret = T(); - for (size_t i=DIM; i--; ret+=lhs[i]*rhs[i]); - return ret; +template std::ostream& operator<<(std::ostream& s, Vec2& v) { + s << "(" << v.x << ", " << v.y << ")\n"; + return s; } - -templatevec operator+(vec lhs, const vec& rhs) { - for (size_t i=DIM; i--; lhs[i]+=rhs[i]); - return lhs; +template std::ostream& operator<<(std::ostream& s, Vec3& v) { + s << "(" << v.x << ", " << v.y << ", " << v.z << ")\n"; + return s; } -templatevec operator-(vec lhs, const vec& rhs) { - for (size_t i=DIM; i--; lhs[i]-=rhs[i]); - return lhs; -} +////////////////////////////////////////////////////////////////////////////////////////////// -template vec operator*(vec lhs, const U& rhs) { - for (size_t i=DIM; i--; lhs[i]*=rhs); - return lhs; -} - -template vec operator/(vec lhs, const U& rhs) { - for (size_t i=DIM; i--; lhs[i]/=rhs); - return lhs; -} - -template vec embed(const vec &v, T fill=1) { - vec ret; - for (size_t i=LEN; i--; ret[i]=(i vec proj(const vec &v) { - vec ret; - for (size_t i=LEN; i--; ret[i]=v[i]); - return ret; -} - -template vec<3,T> cross(vec<3,T> v1, vec<3,T> v2) { - return vec<3,T>(v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x); -} - -template std::ostream& operator<<(std::ostream& out, vec& v) { - for(unsigned int i=0; i struct dt { - static T det(const mat& src) { - T ret=0; - for (size_t i=DIM; i--; ret += src[0][i]*src.cofactor(0,i)); - return ret; - } -}; - -template struct dt<1,T> { - static T det(const mat<1,1,T>& src) { - return src[0][0]; - } -}; - -///////////////////////////////////////////////////////////////////////////////// - -template class mat { - vec rows[DimRows]; +class Matrix { + std::vector > m; + int rows, cols; public: - mat() {} - - vec& operator[] (const size_t idx) { - assert(idx& operator[] (const size_t idx) const { - assert(idx col(const size_t idx) const { - assert(idx ret; - for (size_t i=DimRows; i--; ret[i]=rows[i][idx]); - return ret; - } - - void set_col(size_t idx, vec v) { - assert(idx identity() { - mat ret; - for (size_t i=DimRows; i--; ) - for (size_t j=DimCols;j--; ret[i][j]=(i==j)); - return ret; - } - - T det() const { - return dt::det(*this); - } - - mat get_minor(size_t row, size_t col) const { - mat ret; - for (size_t i=DimRows-1; i--; ) - for (size_t j=DimCols-1;j--; ret[i][j]=rows[i adjugate() const { - mat ret; - for (size_t i=DimRows; i--; ) - for (size_t j=DimCols; j--; ret[i][j]=cofactor(i,j)); - return ret; - } - - mat invert_transpose() { - mat ret = adjugate(); - T tmp = ret[0]*rows[0]; - return ret/tmp; - } - - mat invert() { - return invert_transpose().transpose(); - } - - mat transpose() { - mat ret; - for (size_t i=DimCols; i--; ret[i]=this->col(i)); - return ret; - } + Matrix(int r=4, int c=4); + Matrix(Vec3f v); + int nrows(); + int ncols(); + static Matrix identity(int dimensions); + std::vector& operator[](const int i); + Matrix operator*(const Matrix& a); + Matrix transpose(); + Matrix inverse(); + friend std::ostream& operator<<(std::ostream& s, Matrix& m); }; -///////////////////////////////////////////////////////////////////////////////// -template vec operator*(const mat& lhs, const vec& rhs) { - vec ret; - for (size_t i=DimRows; i--; ret[i]=lhs[i]*rhs); - return ret; -} - -templatemat operator*(const mat& lhs, const mat& rhs) { - mat result; - for (size_t i=R1; i--; ) - for (size_t j=C2; j--; result[i][j]=lhs[i]*rhs.col(j)); - return result; -} - -templatemat operator/(mat lhs, const T& rhs) { - for (size_t i=DimRows; i--; lhs[i]=lhs[i]/rhs); - return lhs; -} - -template std::ostream& operator<<(std::ostream& out, mat& m) { - for (size_t i=0; i Vec2f; -typedef vec<2, int> Vec2i; -typedef vec<3, float> Vec3f; -typedef vec<3, int> Vec3i; -typedef vec<4, float> Vec4f; -typedef mat<4,4,float> Matrix; #endif //__GEOMETRY_H__ - diff --git a/src/external/tests/tinyrenderer/model.cpp b/src/external/tests/tinyrenderer/model.cpp index 3e458e0..fcbba03 100644 --- a/src/external/tests/tinyrenderer/model.cpp +++ b/src/external/tests/tinyrenderer/model.cpp @@ -5,7 +5,7 @@ #include #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, 2, "f ")) { - std::vector f; - int itrash, idx; + } else if (!line.compare(0, 3, "vn ")) { + iss >> trash >> trash; + Vec3f n; + for (int i=0;i<3;i++) iss >> n[i]; + norms_.push_back(n); + } else if (!line.compare(0, 3, "vt ")) { + iss >> trash >> trash; + Vec2f uv; + for (int i=0;i<2;i++) iss >> uv[i]; + uv_.push_back(uv); + } else if (!line.compare(0, 2, "f ")) { + std::vector f; + Vec3i tmp; iss >> trash; - while (iss >> 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 Model::face(int idx) { - return faces_[idx]; + std::vector face; + for (int i=0; i<(int)faces_[idx].size(); i++) face.push_back(faces_[idx][i][0]); + return face; } Vec3f Model::vert(int i) { return verts_[i]; } +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(); +} + + diff --git a/src/external/tests/tinyrenderer/model.h b/src/external/tests/tinyrenderer/model.h index 00346ea..01c9810 100644 --- a/src/external/tests/tinyrenderer/model.h +++ b/src/external/tests/tinyrenderer/model.h @@ -3,18 +3,26 @@ #include #include "geometry.h" +#include "tgaimage.h" class Model { private: - std::vector verts_; - std::vector > faces_; + std::vector verts_; + std::vector > faces_; // attention, this Vec3i means vertex/uv/normal + std::vector norms_; + std::vector 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 vert(int i); - std::vector face(int idx); + 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 face(int idx); }; #endif //__MODEL_H__ diff --git a/src/external/tests/tinyrenderer/tgaimage.cpp b/src/external/tests/tinyrenderer/tgaimage.cpp new file mode 100644 index 0000000..7ec68bc --- /dev/null +++ b/src/external/tests/tinyrenderer/tgaimage.cpp @@ -0,0 +1,357 @@ +#include +#include +#include +#include +#include +#include "tgaimage.h" + +TGAImage::TGAImage() : data(NULL), width(0), height(0), bytespp(0) { +} + +TGAImage::TGAImage(int w, int h, int bpp) : data(NULL), width(w), height(h), bytespp(bpp) { + unsigned long nbytes = width*height*bytespp; + data = new unsigned char[nbytes]; + memset(data, 0, nbytes); +} + +TGAImage::TGAImage(const TGAImage &img) : data(NULL), width(img.width), height(img.height), bytespp(img.bytespp) { + unsigned long nbytes = width*height*bytespp; + data = new unsigned char[nbytes]; + memcpy(data, img.data, nbytes); +} + +TGAImage::~TGAImage() { + if (data) delete [] data; +} + +TGAImage & TGAImage::operator =(const TGAImage &img) { + if (this != &img) { + if (data) delete [] data; + width = img.width; + height = img.height; + bytespp = img.bytespp; + unsigned long nbytes = width*height*bytespp; + data = new unsigned char[nbytes]; + memcpy(data, img.data, nbytes); + } + return *this; +} + +bool TGAImage::read_tga_file(const char *filename) { + if (data) delete [] data; + data = NULL; + std::ifstream in; + in.open (filename, std::ios::binary); + if (!in.is_open()) { + std::cerr << "can't open file " << filename << "\n"; + in.close(); + return false; + } + TGA_Header header; + in.read((char *)&header, sizeof(header)); + if (!in.good()) { + in.close(); + std::cerr << "an error occured while reading the header\n"; + return false; + } + width = header.width; + height = header.height; + bytespp = header.bitsperpixel>>3; + if (width<=0 || height<=0 || (bytespp!=GRAYSCALE && bytespp!=RGB && bytespp!=RGBA)) { + in.close(); + std::cerr << "bad bpp (or width/height) value\n"; + return false; + } + unsigned long nbytes = bytespp*width*height; + data = new unsigned char[nbytes]; + if (3==header.datatypecode || 2==header.datatypecode) { + in.read((char *)data, nbytes); + if (!in.good()) { + in.close(); + std::cerr << "an error occured while reading the data\n"; + return false; + } + } else if (10==header.datatypecode||11==header.datatypecode) { + if (!load_rle_data(in)) { + in.close(); + std::cerr << "an error occured while reading the data\n"; + return false; + } + } else { + in.close(); + std::cerr << "unknown file format " << (int)header.datatypecode << "\n"; + return false; + } + if (!(header.imagedescriptor & 0x20)) { + flip_vertically(); + } + if (header.imagedescriptor & 0x10) { + flip_horizontally(); + } + std::cerr << width << "x" << height << "/" << bytespp*8 << "\n"; + in.close(); + return true; +} + +bool TGAImage::load_rle_data(std::ifstream &in) { + unsigned long pixelcount = width*height; + unsigned long currentpixel = 0; + unsigned long currentbyte = 0; + TGAColor colorbuffer; + do { + unsigned char chunkheader = 0; + chunkheader = in.get(); + if (!in.good()) { + std::cerr << "an error occured while reading the data\n"; + return false; + } + if (chunkheader<128) { + chunkheader++; + for (int i=0; ipixelcount) { + std::cerr << "Too many pixels read\n"; + return false; + } + } + } else { + chunkheader -= 127; + in.read((char *)colorbuffer.bgra, bytespp); + if (!in.good()) { + std::cerr << "an error occured while reading the header\n"; + return false; + } + for (int i=0; ipixelcount) { + std::cerr << "Too many pixels read\n"; + return false; + } + } + } + } while (currentpixel < pixelcount); + return true; +} + +bool TGAImage::write_tga_file(const char *filename, bool rle) { + unsigned char developer_area_ref[4] = {0, 0, 0, 0}; + unsigned char extension_area_ref[4] = {0, 0, 0, 0}; + unsigned char footer[18] = {'T','R','U','E','V','I','S','I','O','N','-','X','F','I','L','E','.','\0'}; + std::ofstream out; + out.open (filename, std::ios::binary); + if (!out.is_open()) { + std::cerr << "can't open file " << filename << "\n"; + out.close(); + return false; + } + TGA_Header header; + memset((void *)&header, 0, sizeof(header)); + header.bitsperpixel = bytespp<<3; + header.width = width; + header.height = height; + header.datatypecode = (bytespp==GRAYSCALE?(rle?11:3):(rle?10:2)); + header.imagedescriptor = 0x20; // top-left origin + out.write((char *)&header, sizeof(header)); + if (!out.good()) { + out.close(); + std::cerr << "can't dump the tga file\n"; + return false; + } + if (!rle) { + out.write((char *)data, width*height*bytespp); + if (!out.good()) { + std::cerr << "can't unload raw data\n"; + out.close(); + return false; + } + } else { + if (!unload_rle_data(out)) { + out.close(); + std::cerr << "can't unload rle data\n"; + return false; + } + } + out.write((char *)developer_area_ref, sizeof(developer_area_ref)); + if (!out.good()) { + std::cerr << "can't dump the tga file\n"; + out.close(); + return false; + } + out.write((char *)extension_area_ref, sizeof(extension_area_ref)); + if (!out.good()) { + std::cerr << "can't dump the tga file\n"; + out.close(); + return false; + } + out.write((char *)footer, sizeof(footer)); + if (!out.good()) { + std::cerr << "can't dump the tga file\n"; + out.close(); + return false; + } + out.close(); + return true; +} + +// TODO: it is not necessary to break a raw chunk for two equal pixels (for the matter of the resulting size) +bool TGAImage::unload_rle_data(std::ofstream &out) { + const unsigned char max_chunk_length = 128; + unsigned long npixels = width*height; + unsigned long curpix = 0; + while (curpix=width || y>=height) { + return TGAColor(); + } + return TGAColor(data+(x+y*width)*bytespp, bytespp); +} + +bool TGAImage::set(int x, int y, TGAColor &c) { + if (!data || x<0 || y<0 || x>=width || y>=height) { + return false; + } + memcpy(data+(x+y*width)*bytespp, c.bgra, bytespp); + return true; +} + +bool TGAImage::set(int x, int y, const TGAColor &c) { + if (!data || x<0 || y<0 || x>=width || y>=height) { + return false; + } + memcpy(data+(x+y*width)*bytespp, c.bgra, bytespp); + return true; +} + +int TGAImage::get_bytespp() { + return bytespp; +} + +int TGAImage::get_width() { + return width; +} + +int TGAImage::get_height() { + return height; +} + +bool TGAImage::flip_horizontally() { + if (!data) return false; + int half = width>>1; + for (int i=0; i>1; + for (int j=0; j=(int)width) { + errx -= width; + nx += bytespp; + memcpy(tdata+nscanline+nx, data+oscanline+ox, bytespp); + } + } + erry += h; + oscanline += olinebytes; + while (erry>=(int)height) { + if (erry>=(int)height<<1) // it means we jump over a scanline + memcpy(tdata+nscanline+nlinebytes, tdata+nscanline, nlinebytes); + erry -= height; + nscanline += nlinebytes; + } + } + delete [] data; + data = tdata; + width = w; + height = h; + return true; +} + diff --git a/src/external/tests/tinyrenderer/tgaimage.h b/src/external/tests/tinyrenderer/tgaimage.h new file mode 100644 index 0000000..07ee9f1 --- /dev/null +++ b/src/external/tests/tinyrenderer/tgaimage.h @@ -0,0 +1,98 @@ +#ifndef __IMAGE_H__ +#define __IMAGE_H__ + +#include + +#pragma pack(push,1) +struct TGA_Header { + char idlength; + char colormaptype; + char datatypecode; + short colormaporigin; + short colormaplength; + char colormapdepth; + short x_origin; + short y_origin; + short width; + short height; + char bitsperpixel; + char imagedescriptor; +}; +#pragma pack(pop) + + + +struct TGAColor { + unsigned char bgra[4]; + unsigned char bytespp; + + TGAColor() : bgra(), bytespp(1) { + for (int i=0; i<4; i++) bgra[i] = 0; + } + + TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) { + bgra[0] = B; + bgra[1] = G; + bgra[2] = R; + bgra[3] = A; + } + + TGAColor(unsigned char v) : bgra(), bytespp(1) { + for (int i=0; i<4; i++) bgra[i] = 0; + bgra[0] = v; + } + + + TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) { + for (int i=0; i<(int)bpp; i++) { + bgra[i] = p[i]; + } + for (int i=bpp; i<4; i++) { + bgra[i] = 0; + } + } + + 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__ diff --git a/src/external/tests/tinyrenderer/tinyrenderer.cpp b/src/external/tests/tinyrenderer/tinyrenderer.cpp new file mode 100644 index 0000000..ded9b17 --- /dev/null +++ b/src/external/tests/tinyrenderer/tinyrenderer.cpp @@ -0,0 +1,215 @@ +#pragma warning(push) +#pragma warning(disable : 4459; disable : 4244) + +#include "geometry.cpp" +#include "model.cpp" +#include "tgaimage.cpp" +#include +#include +#include + +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::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 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) +