Start moving model around in 3d space
This commit is contained in:
parent
4c5f8d43a0
commit
ee31383906
@ -981,11 +981,19 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
|
|||||||
DTRAsset_LoadBitmap(input->api, assetStack,
|
DTRAsset_LoadBitmap(input->api, assetStack,
|
||||||
tempStack, &state->bitmap, "tree00.bmp");
|
tempStack, &state->bitmap, "tree00.bmp");
|
||||||
|
|
||||||
|
#if 1
|
||||||
if (DTRAsset_LoadWavefrontObj(input->api, assetStack, &state->mesh, "african_head.obj"))
|
if (DTRAsset_LoadWavefrontObj(input->api, assetStack, &state->mesh, "african_head.obj"))
|
||||||
{
|
{
|
||||||
DTRAsset_LoadBitmap(input->api, assetStack, tempStack, &state->mesh.tex,
|
DTRAsset_LoadBitmap(input->api, assetStack, tempStack, &state->mesh.tex,
|
||||||
"african_head_diffuse.tga");
|
"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
|
// Init Debug
|
||||||
@ -993,7 +1001,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
|
|||||||
if (DTR_DEBUG)
|
if (DTR_DEBUG)
|
||||||
{
|
{
|
||||||
DebugTestStrToF32Converter();
|
DebugTestStrToF32Converter();
|
||||||
DTRDebug_TestMeshFaceAndVertexParser(&state->mesh);
|
// DTRDebug_TestMeshFaceAndVertexParser(&state->mesh);
|
||||||
|
|
||||||
DqnTempMemStack tmp = DqnMemStack_BeginTempRegion(&memory->tempStack);
|
DqnTempMemStack tmp = DqnMemStack_BeginTempRegion(&memory->tempStack);
|
||||||
DTRBitmap test = {};
|
DTRBitmap test = {};
|
||||||
@ -1048,7 +1056,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
|
|||||||
DTRRenderTransform rotatingXform = DTRRender_DefaultTriangleTransform();
|
DTRRenderTransform rotatingXform = DTRRender_DefaultTriangleTransform();
|
||||||
rotatingXform.rotation = rotation;
|
rotatingXform.rotation = rotation;
|
||||||
|
|
||||||
if (1)
|
if (0)
|
||||||
{
|
{
|
||||||
DTRDebug_BeginCycleCount("DTR_Update_RenderPrimitiveTriangles",
|
DTRDebug_BeginCycleCount("DTR_Update_RenderPrimitiveTriangles",
|
||||||
DTRDebugCycleCount_DTR_Update_RenderPrimitiveTriangles);
|
DTRDebugCycleCount_DTR_Update_RenderPrimitiveTriangles);
|
||||||
@ -1063,16 +1071,23 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
|
|||||||
|
|
||||||
if (1)
|
if (1)
|
||||||
{
|
{
|
||||||
|
LOCAL_PERSIST bool runTinyRendererOnce = true;
|
||||||
|
if (1 && runTinyRendererOnce)
|
||||||
|
{
|
||||||
|
DTRDebug_RunTinyRenderer();
|
||||||
|
runTinyRendererOnce = false;
|
||||||
|
}
|
||||||
|
|
||||||
DTRDebug_BeginCycleCount("DTR_Update_RenderModel", DTRDebugCycleCount_DTR_Update_RenderModel);
|
DTRDebug_BeginCycleCount("DTR_Update_RenderModel", DTRDebugCycleCount_DTR_Update_RenderModel);
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
// Draw Loaded Model
|
// Draw Loaded Model
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
const DqnV3 LIGHT = DqnV3_3i(0, 0, -1);
|
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;
|
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);
|
DTRDebug_EndCycleCount(DTRDebugCycleCount_DTR_Update_RenderModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,6 +162,31 @@ WavefModelFaceInit(i32 capacity = 3, DqnMemAPI memAPI = DqnMemAPI_DefaultUseCall
|
|||||||
return result;
|
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,
|
bool DTRAsset_LoadWavefrontObj(const PlatformAPI api, DqnMemStack *const memStack,
|
||||||
DTRMesh *const mesh, const char *const path)
|
DTRMesh *const mesh, const char *const path)
|
||||||
{
|
{
|
||||||
@ -246,7 +271,7 @@ bool DTRAsset_LoadWavefrontObj(const PlatformAPI api, DqnMemStack *const memStac
|
|||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
char *f32StartPtr = scan;
|
char *f32StartPtr = scan;
|
||||||
for (; *scan != ' ' && *scan != '\n';)
|
for (; *scan != ' ' && *scan != '\n' && *scan != '\r';)
|
||||||
{
|
{
|
||||||
DQN_ASSERT(DqnChar_IsDigit(*scan) || (*scan == '.') || (*scan == '-') ||
|
DQN_ASSERT(DqnChar_IsDigit(*scan) || (*scan == '.') || (*scan == '-') ||
|
||||||
*scan == 'e');
|
*scan == 'e');
|
||||||
@ -257,13 +282,12 @@ bool DTRAsset_LoadWavefrontObj(const PlatformAPI api, DqnMemStack *const memStac
|
|||||||
v4.e[vIndex++] = Dqn_StrToF32(f32StartPtr, f32Len);
|
v4.e[vIndex++] = Dqn_StrToF32(f32StartPtr, f32Len);
|
||||||
DQN_ASSERT(vIndex < DQN_ARRAY_COUNT(v4.e));
|
DQN_ASSERT(vIndex < DQN_ARRAY_COUNT(v4.e));
|
||||||
|
|
||||||
while (scan && (*scan == ' ' || *scan == '\n')) scan++;
|
scan += FindFirstCharNotLinefeedOrSpace(scan);
|
||||||
|
|
||||||
if (!scan) break;
|
if (!scan) break;
|
||||||
if (!(DqnChar_IsDigit(*scan) || *scan == '-')) break;
|
if (!(DqnChar_IsDigit(*scan) || *scan == '-')) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_ASSERT(vIndex == 3 || vIndex == 4);
|
DQN_ASSERT(vIndex >= 2 && vIndex <= 4);
|
||||||
if (type == WavefVertexType_Geometric)
|
if (type == WavefVertexType_Geometric)
|
||||||
{
|
{
|
||||||
DqnArray_Push(&obj->geometryArray, v4);
|
DqnArray_Push(&obj->geometryArray, v4);
|
||||||
@ -325,7 +349,7 @@ bool DTRAsset_LoadWavefrontObj(const PlatformAPI api, DqnMemStack *const memStac
|
|||||||
case 'f':
|
case 'f':
|
||||||
{
|
{
|
||||||
scan++;
|
scan++;
|
||||||
while (scan && (*scan == ' ' || *scan == '\n')) scan++;
|
scan += FindFirstCharNotLinefeedOrSpace(scan);
|
||||||
if (!scan) continue;
|
if (!scan) continue;
|
||||||
|
|
||||||
WavefModelFace face = WavefModelFaceInit(3, memAPI);
|
WavefModelFace face = WavefModelFaceInit(3, memAPI);
|
||||||
@ -373,9 +397,7 @@ bool DTRAsset_LoadWavefrontObj(const PlatformAPI api, DqnMemStack *const memStac
|
|||||||
|
|
||||||
if (scan)
|
if (scan)
|
||||||
{
|
{
|
||||||
// Move to next "non-empty" character
|
scan += FindFirstCharNotLinefeedOrSpace(scan);
|
||||||
while (scan && (*scan == ' ' || *scan == '\n'))
|
|
||||||
scan++;
|
|
||||||
|
|
||||||
// If it isn't a digit, then we've read all the
|
// If it isn't a digit, then we've read all the
|
||||||
// vertexes for this face
|
// vertexes for this face
|
||||||
@ -401,14 +423,12 @@ bool DTRAsset_LoadWavefrontObj(const PlatformAPI api, DqnMemStack *const memStac
|
|||||||
case 'g':
|
case 'g':
|
||||||
{
|
{
|
||||||
scan++;
|
scan++;
|
||||||
while (scan && (*scan == ' ' || *scan == '\n')) scan++;
|
scan += FindFirstCharNotLinefeedOrSpace(scan);
|
||||||
|
|
||||||
if (!scan) continue;
|
if (!scan) continue;
|
||||||
|
|
||||||
// Iterate to end of the name, i.e. move ptr to first space
|
// Iterate to end of the name, i.e. move ptr to first space
|
||||||
char *namePtr = scan;
|
char *namePtr = scan;
|
||||||
while (scan && (*scan != ' ' && *scan != '\n'))
|
scan += FindFirstCharNotLinefeedOrSpace(scan);
|
||||||
scan++;
|
|
||||||
|
|
||||||
if (scan)
|
if (scan)
|
||||||
{
|
{
|
||||||
@ -428,8 +448,7 @@ bool DTRAsset_LoadWavefrontObj(const PlatformAPI api, DqnMemStack *const memStac
|
|||||||
obj->groupName[obj->groupNameIndex - 1][i] = namePtr[i];
|
obj->groupName[obj->groupNameIndex - 1][i] = namePtr[i];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
while (scan && (*scan == ' ' || *scan == '\n'))
|
scan += FindFirstCharNotLinefeedOrSpace(scan);
|
||||||
scan++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
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.
|
// not to be used it can be specified as "off" or a value of 0.
|
||||||
case 's':
|
case 's':
|
||||||
{
|
{
|
||||||
// Advance to first non space char after identifier
|
|
||||||
scan++;
|
scan++;
|
||||||
while (scan && *scan == ' ' || *scan == '\n') scan++;
|
scan += FindFirstCharNotLinefeedOrSpace(scan);
|
||||||
|
|
||||||
if (scan && DqnChar_IsDigit(*scan))
|
if (scan && DqnChar_IsDigit(*scan))
|
||||||
{
|
{
|
||||||
char *numStartPtr = scan;
|
char *numStartPtr = scan;
|
||||||
while (scan && (*scan != ' ' && *scan != '\n'))
|
while (scan && (*scan != ' ' && *scan != '\n' && *scan != '\r'))
|
||||||
{
|
{
|
||||||
DQN_ASSERT(DqnChar_IsDigit(*scan));
|
DQN_ASSERT(DqnChar_IsDigit(*scan));
|
||||||
scan++;
|
scan++;
|
||||||
@ -457,26 +475,25 @@ bool DTRAsset_LoadWavefrontObj(const PlatformAPI api, DqnMemStack *const memStac
|
|||||||
obj->groupSmoothing = groupSmoothing;
|
obj->groupSmoothing = groupSmoothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (scan && *scan == ' ' || *scan == '\n') scan++;
|
scan += FindFirstCharNotLinefeedOrSpace(scan);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Comment
|
// Comment
|
||||||
case '#':
|
case '#':
|
||||||
{
|
{
|
||||||
// Skip comment line until new line
|
scan += FindFirstNewlineFeedChar(scan);
|
||||||
while (scan && *scan != '\n')
|
scan += FindFirstCharNotLinefeedOrSpace(scan);
|
||||||
scan++;
|
|
||||||
|
|
||||||
// Skip new lines and any leading white spaces
|
|
||||||
while (scan && (*scan == '\n' || *scan == ' '))
|
|
||||||
scan++;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
@ -723,9 +740,6 @@ FILE_SCOPE void *STBImageMalloc(size_t size)
|
|||||||
#define STBI_FREE(ptr) STBImageFree(ptr)
|
#define STBI_FREE(ptr) STBImageFree(ptr)
|
||||||
|
|
||||||
#define STBI_NO_STDIO
|
#define STBI_NO_STDIO
|
||||||
#define STBI_ONLY_PNG
|
|
||||||
#define STBI_ONLY_TGA
|
|
||||||
#define STBI_ONLY_BMP
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include "external/stb_image.h"
|
#include "external/stb_image.h"
|
||||||
|
|
||||||
@ -755,7 +769,12 @@ bool DTRAsset_LoadBitmap(const PlatformAPI api, DqnMemStack *const memStack,
|
|||||||
bitmap->bytesPerPixel = FORCE_4_BPP;
|
bitmap->bytesPerPixel = FORCE_4_BPP;
|
||||||
u8 *pixels = stbi_load_from_memory(rawData, (i32)file.size, &bitmap->dim.w, &bitmap->dim.h,
|
u8 *pixels = stbi_load_from_memory(rawData, (i32)file.size, &bitmap->dim.w, &bitmap->dim.h,
|
||||||
NULL, FORCE_4_BPP);
|
NULL, FORCE_4_BPP);
|
||||||
if (!pixels) goto cleanup;
|
if (!pixels)
|
||||||
|
{
|
||||||
|
const char *failReason = stbi_failure_reason();
|
||||||
|
api.Print(failReason);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
result = true;
|
result = true;
|
||||||
|
|
||||||
// TODO(doyle): See above. Since we use temp stack we can allocate straight into the AssetStack.
|
// TODO(doyle): See above. Since we use temp stack we can allocate straight into the AssetStack.
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
|
|
||||||
#include "dqn.h"
|
#include "dqn.h"
|
||||||
|
|
||||||
#include "external/tests/tinyrenderer/geometry.h"
|
#if DTR_DEBUG
|
||||||
#include "external/tests/tinyrenderer/model.cpp"
|
#include "external/tests/tinyrenderer/tinyrenderer.cpp"
|
||||||
#include <vector>
|
#endif
|
||||||
|
|
||||||
DTRDebug globalDebug;
|
DTRDebug globalDebug;
|
||||||
void DTRDebug_TestMeshFaceAndVertexParser(DTRMesh *const mesh)
|
void DTRDebug_TestMeshFaceAndVertexParser(DTRMesh *const mesh)
|
||||||
@ -16,12 +16,12 @@ void DTRDebug_TestMeshFaceAndVertexParser(DTRMesh *const mesh)
|
|||||||
if (DTR_DEBUG)
|
if (DTR_DEBUG)
|
||||||
{
|
{
|
||||||
if (!mesh) DQN_ASSERT(DQN_INVALID_CODE_PATH);
|
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());
|
DQN_ASSERT((i64)mesh->numFaces == tmpModel.nfaces());
|
||||||
for (i32 i = 0; i < model.nfaces(); i++)
|
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];
|
DTRMeshFace *myFace = &mesh->faces[i];
|
||||||
|
|
||||||
DQN_ASSERT(myFace->numVertexIndex == correctFace.size());
|
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
|
// Ensure the vertex index references are correct per face
|
||||||
DQN_ASSERT(myFace->vertexIndex[j] == correctFace[j]);
|
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 correctVertex = DqnV3_3f(tmp[0], tmp[1], tmp[2]);
|
||||||
DqnV3 myVertex = (mesh->vertexes[myFace->vertexIndex[j]]).xyz;
|
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, ...)
|
void DTRDebug_PushText(const char *const formatStr, ...)
|
||||||
{
|
{
|
||||||
if (DTR_DEBUG)
|
if (DTR_DEBUG)
|
||||||
|
@ -106,6 +106,7 @@ extern DTRDebug globalDebug;
|
|||||||
|
|
||||||
void DTRDebug_TestMeshFaceAndVertexParser(struct DTRMesh *const mesh);
|
void DTRDebug_TestMeshFaceAndVertexParser(struct DTRMesh *const mesh);
|
||||||
void DTRDebug_DumpZBuffer (struct DTRRenderBuffer *const renderBuffer, struct DqnMemStack *const transMemStack);
|
void DTRDebug_DumpZBuffer (struct DTRRenderBuffer *const renderBuffer, struct DqnMemStack *const transMemStack);
|
||||||
|
void DTRDebug_RunTinyRenderer ();
|
||||||
void DTRDebug_PushText (const char *const formatStr, ...);
|
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 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);
|
void inline DTRDebug_BeginCycleCount (char *title, enum DTRDebugCycleCount tag);
|
||||||
|
@ -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) \
|
#define DEBUG_SIMD_AUTO_CHOOSE_BEGIN_CYCLE_COUNT(type) \
|
||||||
do \
|
do \
|
||||||
{ \
|
{ \
|
||||||
@ -778,6 +770,58 @@ FILE_SCOPE void SIMDTexturedTriangle(DTRRenderBuffer *const renderBuffer, const
|
|||||||
DTRDebug_EndCycleCount(DTRDebugCycleCount_SIMD##type); \
|
DTRDebug_EndCycleCount(DTRDebugCycleCount_SIMD##type); \
|
||||||
} while (0)
|
} 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);
|
||||||
|
|
||||||
DEBUG_SIMD_AUTO_CHOOSE_BEGIN_CYCLE_COUNT(Triangle_Preamble);
|
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_X_PIXELS_TO_SIMD = 2;
|
||||||
const u32 NUM_Y_PIXELS_TO_SIMD = 1;
|
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
|
// SignedArea: _mm_set_ps(unused, p3, p2, p1) ie 0=p1, 1=p1, 2=p3, 3=unused
|
||||||
__m128 signedAreaPixel1;
|
__m128 signedAreaPixel1;
|
||||||
__m128 signedAreaPixel2;
|
__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);
|
__m128 triangleZ = _mm_set_ps(0, p3.z, p2.z, p1.z);
|
||||||
{
|
{
|
||||||
DEBUG_SIMD_AUTO_CHOOSE_BEGIN_CYCLE_COUNT(Triangle_Preamble_SArea);
|
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);
|
DqnV2 startP = DqnV2_V2i(min);
|
||||||
#if 1
|
|
||||||
f32 signedArea1Start = Triangle2TimesSignedArea(p2.xy, p3.xy, startP);
|
f32 signedArea1Start = Triangle2TimesSignedArea(p2.xy, p3.xy, startP);
|
||||||
f32 signedArea1DeltaX = p2.y - p3.y;
|
f32 signedArea1DeltaX = p2.y - p3.y;
|
||||||
f32 signedArea1DeltaY = p3.x - p2.x;
|
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 signedArea3Start = Triangle2TimesSignedArea(p1.xy, p2.xy, startP);
|
||||||
f32 signedArea3DeltaX = p1.y - p2.y;
|
f32 signedArea3DeltaX = p1.y - p2.y;
|
||||||
f32 signedArea3DeltaY = p2.x - p1.x;
|
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();
|
DTR_DEBUG_EP_TIMED_END_BLOCK();
|
||||||
DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle_Preamble_SArea);
|
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 uv2SubUv1 = uv2 - uv1;
|
||||||
const DqnV2 uv3SubUv1 = uv3 - uv1;
|
const DqnV2 uv3SubUv1 = uv3 - uv1;
|
||||||
const u32 texturePitch = (texture) ? (texture->bytesPerPixel * texture->dim.w) : 0;
|
|
||||||
const u8 *const texturePtr = (texture) ? (texture->memory) : NULL;
|
#define UNROLL_LOOP 1
|
||||||
const u32 zBufferPitch = renderBuffer->width;
|
|
||||||
DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle_Preamble);
|
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
|
// 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)
|
for (i32 bufferX = min.x; bufferX < max.x; bufferX += NUM_X_PIXELS_TO_SIMD)
|
||||||
{
|
{
|
||||||
|
#if UNROLL_LOOP
|
||||||
// Rasterise buffer(X, Y) pixel
|
// Rasterise buffer(X, Y) pixel
|
||||||
{
|
{
|
||||||
__m128 checkArea = signedArea1;
|
__m128 checkArea = signedArea1;
|
||||||
@ -952,8 +982,17 @@ FILE_SCOPE void SIMDTexturedTriangle(DTRRenderBuffer *const renderBuffer, const
|
|||||||
}
|
}
|
||||||
signedArea2 = _mm_add_ps(signedArea2, signedAreaPixelDeltaX);
|
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);
|
signedAreaPixel1 = _mm_add_ps(signedAreaPixel1, signedAreaPixelDeltaY);
|
||||||
signedAreaPixel2 = _mm_add_ps(signedAreaPixel2, 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);
|
DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle);
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE_SCOPE void SlowTexturedTriangle(DTRRenderBuffer *const renderBuffer, const DqnV3 p1,
|
FILE_SCOPE void SlowTriangle(DTRRenderBuffer * const renderBuffer, const DqnV3 p1, const DqnV3 p2,
|
||||||
const DqnV3 p2, const DqnV3 p3, const DqnV2 uv1,
|
const DqnV3 p3, const DqnV2 uv1, const DqnV2 uv2, const DqnV2 uv3,
|
||||||
const DqnV2 uv2, const DqnV2 uv3, DTRBitmap *const texture,
|
DTRBitmap *const texture, DqnV4 color, const DqnV2i min,
|
||||||
DqnV4 color, const DqnV2i min, const DqnV2i max)
|
const DqnV2i max)
|
||||||
{
|
{
|
||||||
DTR_DEBUG_EP_TIMED_FUNCTION();
|
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);
|
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,
|
void DTRRender_TexturedTriangle(DTRRenderBuffer *const renderBuffer, DqnV3 p1, DqnV3 p2, DqnV3 p3,
|
||||||
DqnV2 uv1, DqnV2 uv2, DqnV2 uv3, DTRBitmap *const texture,
|
DqnV2 uv1, DqnV2 uv2, DqnV2 uv3, DTRBitmap *const texture,
|
||||||
DqnV4 color, const DTRRenderTransform transform)
|
DqnV4 color, const DTRRenderTransform transform)
|
||||||
@ -1114,6 +1207,7 @@ void DTRRender_TexturedTriangle(DTRRenderBuffer *const renderBuffer, DqnV3 p1, D
|
|||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
Make3PointsClockwise(&p1, &p2, &p3);
|
Make3PointsClockwise(&p1, &p2, &p3);
|
||||||
|
|
||||||
|
#if 0
|
||||||
// TODO(doyle): Transform is only in 2d right now
|
// TODO(doyle): Transform is only in 2d right now
|
||||||
DqnV2 origin = Get2DOriginFromTransformAnchor(p1.xy, p2.xy, p3.xy, transform);
|
DqnV2 origin = Get2DOriginFromTransformAnchor(p1.xy, p2.xy, p3.xy, transform);
|
||||||
DqnV2 pList[] = {p1.xy - origin, p2.xy - origin, p3.xy - origin};
|
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];
|
p1.xy = pList[0];
|
||||||
p2.xy = pList[1];
|
p2.xy = pList[1];
|
||||||
p3.xy = pList[2];
|
p3.xy = pList[2];
|
||||||
|
#else
|
||||||
|
DqnV2 pList[] = {p1.xy, p2.xy, p3.xy};
|
||||||
|
#endif
|
||||||
|
|
||||||
DqnRect bounds = GetBoundingBox(pList, DQN_ARRAY_COUNT(pList));
|
DqnRect bounds = GetBoundingBox(pList, DQN_ARRAY_COUNT(pList));
|
||||||
DqnRect screenSpace = DqnRect_4i(0, 0, renderBuffer->width - 1, renderBuffer->height - 1);
|
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)
|
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
|
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,
|
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;
|
if (!mesh) return;
|
||||||
|
|
||||||
|
Test();
|
||||||
|
|
||||||
for (u32 i = 0; i < mesh->numFaces; i++)
|
for (u32 i = 0; i < mesh->numFaces; i++)
|
||||||
{
|
{
|
||||||
DTRMeshFace face = mesh->faces[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 vertA = mesh->vertexes[vertAIndex];
|
||||||
DqnV4 vertB = mesh->vertexes[vertBIndex];
|
DqnV4 vertB = mesh->vertexes[vertBIndex];
|
||||||
DqnV4 vertC = mesh->vertexes[vertCIndex];
|
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
|
// TODO(doyle): Some models have -ve indexes to refer to relative
|
||||||
// vertices. We should resolve that to positive indexes at run time.
|
// vertices. We should resolve that to positive indexes at run time.
|
||||||
DQN_ASSERT(vertAIndex < (i32)mesh->numVertexes);
|
DQN_ASSERT(vertAIndex < (i32)mesh->numVertexes);
|
||||||
DQN_ASSERT(vertBIndex < (i32)mesh->numVertexes);
|
DQN_ASSERT(vertBIndex < (i32)mesh->numVertexes);
|
||||||
DQN_ASSERT(vertCIndex < (i32)mesh->numVertexes);
|
DQN_ASSERT(vertCIndex < (i32)mesh->numVertexes);
|
||||||
|
|
||||||
|
// TODO(doyle): Use normals from model
|
||||||
DqnV4 vertAB = vertB - vertA;
|
DqnV4 vertAB = vertB - vertA;
|
||||||
DqnV4 vertAC = vertC - vertA;
|
DqnV4 vertAC = vertC - vertA;
|
||||||
DqnV3 normal = DqnV3_Cross(vertAC.xyz, vertAB.xyz);
|
DqnV3 normal = DqnV3_Cross(vertAC.xyz, vertAB.xyz);
|
||||||
|
|
||||||
f32 intensity = DqnV3_Dot(DqnV3_Normalise(normal), lightVector);
|
f32 intensity = DqnV3_Dot(DqnV3_Normalise(normal), lightVector);
|
||||||
if (intensity < 0) continue;
|
|
||||||
DqnV4 modelCol = DqnV4_4f(1, 1, 1, 1);
|
DqnV4 modelCol = DqnV4_4f(1, 1, 1, 1);
|
||||||
modelCol.rgb *= DQN_ABS(intensity);
|
modelCol.rgb *= DQN_ABS(intensity);
|
||||||
|
|
||||||
DqnV3 screenVA = (vertA.xyz * scale) + pos;
|
// Apply vertex shader, model view projection
|
||||||
DqnV3 screenVB = (vertB.xyz * scale) + pos;
|
DqnMat4 modelMatrix = {};
|
||||||
DqnV3 screenVC = (vertC.xyz * scale) + pos;
|
{
|
||||||
|
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
|
DqnV3 eye = DqnV3_3f(0, 0, 1);
|
||||||
// I don't do any interpolation in the triangle routine for jagged
|
DqnV3 up = DqnV3_3f(0, 1, 0);
|
||||||
// edges.
|
DqnV3 center = DqnV3_3f(0, 0, 0);
|
||||||
#if 1
|
|
||||||
screenVA.x = (f32)(i32)(screenVA.x + 0.5f);
|
DqnMat4 viewMatrix = DqnMat4_LookAt(eye, center, up);
|
||||||
screenVA.y = (f32)(i32)(screenVA.y + 0.5f);
|
|
||||||
screenVB.x = (f32)(i32)(screenVB.x + 0.5f);
|
f32 aspectRatio = (f32)renderBuffer->width / (f32)renderBuffer->height;
|
||||||
screenVB.y = (f32)(i32)(screenVB.y + 0.5f);
|
DqnMat4 perspective = DqnMat4_Perspective(80.0f, aspectRatio, 0.5f, 100.0f);
|
||||||
screenVC.x = (f32)(i32)(screenVC.x + 0.5f);
|
perspective = DqnMat4_Identity();
|
||||||
screenVC.y = (f32)(i32)(screenVC.y + 0.5f);
|
perspective.e[2][3] = -1.0f / DqnV3_Length(eye, center);
|
||||||
#endif
|
|
||||||
|
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 textureAIndex = face.texIndex[0];
|
||||||
i32 textureBIndex = face.texIndex[1];
|
i32 textureBIndex = face.texIndex[1];
|
||||||
@ -1216,11 +1400,11 @@ void DTRRender_Mesh(DTRRenderBuffer *const renderBuffer, DTRMesh *const mesh, co
|
|||||||
bool DEBUG_SIMPLE_MODE = false;
|
bool DEBUG_SIMPLE_MODE = false;
|
||||||
if (DTR_DEBUG && DEBUG_SIMPLE_MODE)
|
if (DTR_DEBUG && DEBUG_SIMPLE_MODE)
|
||||||
{
|
{
|
||||||
DTRRender_Triangle(renderBuffer, screenVA, screenVB, screenVC, modelCol);
|
DTRRender_Triangle(renderBuffer, vertA.xyz, vertB.xyz, vertC.xyz, modelCol);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DTRRender_TexturedTriangle(renderBuffer, screenVA, screenVB, screenVC, texA, texB,
|
DTRRender_TexturedTriangle(renderBuffer, vertA.xyz, vertB.xyz, vertC.xyz, texA, texB,
|
||||||
texC, &mesh->tex, modelCol);
|
texC, &mesh->tex, modelCol);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1228,11 +1412,11 @@ void DTRRender_Mesh(DTRRenderBuffer *const renderBuffer, DTRMesh *const mesh, co
|
|||||||
if (DTR_DEBUG && DEBUG_WIREFRAME)
|
if (DTR_DEBUG && DEBUG_WIREFRAME)
|
||||||
{
|
{
|
||||||
DqnV4 wireColor = DqnV4_4f(1.0f, 1.0f, 1.0f, 0.01f);
|
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);
|
wireColor);
|
||||||
DTRRender_Line(renderBuffer, DqnV2i_V2(screenVB.xy), DqnV2i_V2(screenVC.xy),
|
DTRRender_Line(renderBuffer, DqnV2i_V2(vertB.xy), DqnV2i_V2(vertC.xy),
|
||||||
wireColor);
|
wireColor);
|
||||||
DTRRender_Line(renderBuffer, DqnV2i_V2(screenVC.xy), DqnV2i_V2(screenVA.xy),
|
DTRRender_Line(renderBuffer, DqnV2i_V2(vertC.xy), DqnV2i_V2(vertA.xy),
|
||||||
wireColor);
|
wireColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ REM wd4100 unused argument parameters
|
|||||||
REM wd4201 nonstandard extension used: nameless struct/union
|
REM wd4201 nonstandard extension used: nameless struct/union
|
||||||
REM wd4189 local variable is initialised but not referenced
|
REM wd4189 local variable is initialised but not referenced
|
||||||
REM wd4505 unreferenced local function not used will be removed
|
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 DLLFlags=/Fm%ProjectName% /Fo%ProjectName% /Fa%ProjectName% /Fe%ProjectName%
|
||||||
set Win32Flags=/FmWin32DTRenderer /FeWin32DTRenderer
|
set Win32Flags=/FmWin32DTRenderer /FeWin32DTRenderer
|
||||||
|
|
||||||
|
93
src/dqn.h
93
src/dqn.h
@ -529,8 +529,9 @@ typedef union DqnV3i
|
|||||||
} DqnV3i;
|
} DqnV3i;
|
||||||
|
|
||||||
// DqnV3
|
// 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_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_Add (DqnV3 a, DqnV3 b);
|
||||||
DQN_FILE_SCOPE DqnV3 DqnV3_Sub (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_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_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_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, 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, 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, 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, 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, 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, i32 b) { return (a = DqnV3_Scalei (a, b)); }
|
||||||
@ -580,6 +585,7 @@ typedef union DqnV4 {
|
|||||||
// Create a vector using ints and typecast to floats
|
// 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_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_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_1f(f32 xyzw);
|
||||||
|
|
||||||
DQN_FILE_SCOPE DqnV4 DqnV4_Add (DqnV4 a, DqnV4 b);
|
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
|
typedef union DqnMat4
|
||||||
{
|
{
|
||||||
|
// TODO(doyle): Row/column instead? More cache friendly since multiplication
|
||||||
|
// prefers rows.
|
||||||
DqnV4 col[4];
|
DqnV4 col[4];
|
||||||
// Column/row
|
f32 e[4][4]; // Column/row
|
||||||
f32 e[4][4];
|
|
||||||
} DqnMat4;
|
} DqnMat4;
|
||||||
|
|
||||||
DQN_FILE_SCOPE DqnMat4 DqnMat4_Identity ();
|
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_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_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_Scale (f32 x, f32 y, f32 z);
|
||||||
@ -2065,18 +2073,21 @@ DQN_FILE_SCOPE bool DqnV2i_Equals(DqnV2i a, DqnV2i b)
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Vec3
|
// 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)
|
DQN_FILE_SCOPE DqnV3 DqnV3_3f(f32 x, f32 y, f32 z)
|
||||||
{
|
{
|
||||||
DqnV3 result = {};
|
DqnV3 result = {x, y, z};
|
||||||
result.x = x;
|
|
||||||
result.y = y;
|
|
||||||
result.z = z;
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_FILE_SCOPE DqnV3 DqnV3_3i(i32 x, i32 y, i32 z)
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2174,6 +2185,24 @@ DQN_FILE_SCOPE DqnV3 DqnV3_Normalise(DqnV3 a)
|
|||||||
return result;
|
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)
|
DQN_FILE_SCOPE DqnV3i DqnV3i_3i(i32 x, i32 y, i32 z)
|
||||||
{
|
{
|
||||||
DqnV3i result = {x, y, 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;
|
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)
|
DQN_FILE_SCOPE DqnV4 DqnV4_1f(f32 xyzw)
|
||||||
{
|
{
|
||||||
DqnV4 result = {xyzw, xyzw, xyzw, xyzw};
|
DqnV4 result = {xyzw, xyzw, xyzw, xyzw};
|
||||||
@ -2289,8 +2326,8 @@ DQN_FILE_SCOPE DqnMat4 DqnMat4_Identity()
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_FILE_SCOPE DqnMat4
|
DQN_FILE_SCOPE DqnMat4 DqnMat4_Orthographic(f32 left, f32 right, f32 bottom, f32 top, f32 zNear,
|
||||||
DqnMat4_Ortho(f32 left, f32 right, f32 bottom, f32 top, f32 zNear, f32 zFar)
|
f32 zFar)
|
||||||
{
|
{
|
||||||
DqnMat4 result = DqnMat4_Identity();
|
DqnMat4 result = DqnMat4_Identity();
|
||||||
result.e[0][0] = +2.0f / (right - left);
|
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;
|
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)
|
DQN_FILE_SCOPE DqnMat4 DqnMat4_Translate(f32 x, f32 y, f32 z)
|
||||||
{
|
{
|
||||||
DqnMat4 result = DqnMat4_Identity();
|
DqnMat4 result = DqnMat4_Identity();
|
||||||
@ -2350,7 +2405,8 @@ DQN_FILE_SCOPE DqnMat4 DqnMat4_Mul(DqnMat4 a, DqnMat4 b)
|
|||||||
{
|
{
|
||||||
DqnMat4 result = {0};
|
DqnMat4 result = {0};
|
||||||
for (i32 j = 0; j < 4; j++) {
|
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]
|
result.e[j][i] = a.e[0][i] * b.e[j][0]
|
||||||
+ a.e[1][i] * b.e[j][1]
|
+ a.e[1][i] * b.e[j][1]
|
||||||
+ a.e[2][i] * b.e[j][2]
|
+ 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)
|
DQN_FILE_SCOPE DqnV4 DqnMat4_MulV4(DqnMat4 a, DqnV4 b)
|
||||||
{
|
{
|
||||||
DqnV4 result = {0};
|
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.x = (a.e[0][0] * b.x) + (a.e[1][0] * b.y) + (a.e[2][0] * b.z) +
|
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);
|
||||||
(a.e[3][0] * 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.y = (a.e[0][1] * b.x) + (a.e[1][1] * b.y) + (a.e[2][1] * b.z) +
|
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);
|
||||||
(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;
|
return result;
|
||||||
}
|
}
|
||||||
|
116
src/external/tests/tinyrenderer/geometry.cpp
vendored
Normal file
116
src/external/tests/tinyrenderer/geometry.cpp
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
|
250
src/external/tests/tinyrenderer/geometry.h
vendored
250
src/external/tests/tinyrenderer/geometry.h
vendored
@ -1,221 +1,75 @@
|
|||||||
#ifndef __GEOMETRY_H__
|
#ifndef __GEOMETRY_H__
|
||||||
#define __GEOMETRY_H__
|
#define __GEOMETRY_H__
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <vector>
|
#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 {
|
template <class t> struct Vec2 {
|
||||||
vec() { for (size_t i=DIM; i--; data_[i] = T()); }
|
t x, y;
|
||||||
T& operator[](const size_t i) { assert(i<DIM); return data_[i]; }
|
Vec2<t>() : x(t()), y(t()) {}
|
||||||
const T& operator[](const size_t i) const { assert(i<DIM); return data_[i]; }
|
Vec2<t>(t _x, t _y) : x(_x), y(_y) {}
|
||||||
private:
|
Vec2<t> operator +(const Vec2<t> &V) const { return Vec2<t>(x+V.x, y+V.y); }
|
||||||
T data_[DIM];
|
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 <class t> struct Vec3 {
|
||||||
|
t x, y, z;
|
||||||
template <typename T> struct vec<2,T> {
|
Vec3<t>() : x(t()), y(t()), z(t()) { }
|
||||||
vec() : x(T()), y(T()) {}
|
Vec3<t>(t _x, t _y, t _z) : x(_x), y(_y), z(_z) {}
|
||||||
vec(T X, T Y) : x(X), y(Y) {}
|
Vec3<t>(Matrix m);
|
||||||
template <class U> vec<2,T>(const vec<2,U> &v);
|
template <class u> Vec3<t>(const Vec3<u> &v);
|
||||||
T& operator[](const size_t i) { assert(i<2); return i<=0 ? x : y; }
|
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); }
|
||||||
const T& operator[](const size_t i) const { assert(i<2); return i<=0 ? x : y; }
|
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); }
|
||||||
T x,y;
|
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> {
|
template <> template <> Vec3<int>::Vec3(const Vec3<float> &v);
|
||||||
vec() : x(T()), y(T()), z(T()) {}
|
template <> template <> Vec3<float>::Vec3(const Vec3<int> &v);
|
||||||
vec(T X, T Y, T Z) : x(X), y(Y), z(Z) {}
|
|
||||||
template <class U> vec<3,T>(const vec<3,U> &v);
|
|
||||||
T& operator[](const size_t i) { assert(i<3); return i<=0 ? x : (1==i ? y : z); }
|
|
||||||
const T& operator[](const size_t i) const { assert(i<3); return i<=0 ? x : (1==i ? y : z); }
|
|
||||||
float norm() { return std::sqrt(x*x+y*y+z*z); }
|
|
||||||
vec<3,T> & normalize(T l=1) { *this = (*this)*(l/norm()); return *this; }
|
|
||||||
|
|
||||||
T x,y,z;
|
|
||||||
};
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////
|
template <class t> std::ostream& operator<<(std::ostream& s, Vec2<t>& v) {
|
||||||
|
s << "(" << v.x << ", " << v.y << ")\n";
|
||||||
template<size_t DIM,typename T> T operator*(const vec<DIM,T>& lhs, const vec<DIM,T>& rhs) {
|
return s;
|
||||||
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, Vec3<t>& v) {
|
||||||
template<size_t DIM,typename T>vec<DIM,T> operator+(vec<DIM,T> lhs, const vec<DIM,T>& rhs) {
|
s << "(" << v.x << ", " << v.y << ", " << v.z << ")\n";
|
||||||
for (size_t i=DIM; i--; lhs[i]+=rhs[i]);
|
return s;
|
||||||
return lhs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t DIM,typename T>vec<DIM,T> operator-(vec<DIM,T> lhs, const vec<DIM,T>& rhs) {
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
for (size_t i=DIM; i--; lhs[i]-=rhs[i]);
|
|
||||||
return lhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<size_t DIM,typename T,typename U> vec<DIM,T> operator*(vec<DIM,T> lhs, const U& rhs) {
|
class Matrix {
|
||||||
for (size_t i=DIM; i--; lhs[i]*=rhs);
|
std::vector<std::vector<float> > m;
|
||||||
return lhs;
|
int rows, cols;
|
||||||
}
|
|
||||||
|
|
||||||
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];
|
|
||||||
public:
|
public:
|
||||||
mat() {}
|
Matrix(int r=4, int c=4);
|
||||||
|
Matrix(Vec3f v);
|
||||||
vec<DimCols,T>& operator[] (const size_t idx) {
|
int nrows();
|
||||||
assert(idx<DimRows);
|
int ncols();
|
||||||
return rows[idx];
|
static Matrix identity(int dimensions);
|
||||||
}
|
std::vector<float>& operator[](const int i);
|
||||||
|
Matrix operator*(const Matrix& a);
|
||||||
const vec<DimCols,T>& operator[] (const size_t idx) const {
|
Matrix transpose();
|
||||||
assert(idx<DimRows);
|
Matrix inverse();
|
||||||
return rows[idx];
|
friend std::ostream& operator<<(std::ostream& s, Matrix& m);
|
||||||
}
|
|
||||||
|
|
||||||
vec<DimRows,T> col(const size_t idx) const {
|
|
||||||
assert(idx<DimCols);
|
|
||||||
vec<DimRows,T> ret;
|
|
||||||
for (size_t i=DimRows; i--; ret[i]=rows[i][idx]);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_col(size_t idx, vec<DimRows,T> v) {
|
|
||||||
assert(idx<DimCols);
|
|
||||||
for (size_t i=DimRows; i--; rows[i][idx]=v[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static mat<DimRows,DimCols,T> identity() {
|
|
||||||
mat<DimRows,DimCols,T> ret;
|
|
||||||
for (size_t i=DimRows; i--; )
|
|
||||||
for (size_t j=DimCols;j--; ret[i][j]=(i==j));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
T det() const {
|
|
||||||
return dt<DimCols,T>::det(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
mat<DimRows-1,DimCols-1,T> get_minor(size_t row, size_t col) const {
|
|
||||||
mat<DimRows-1,DimCols-1,T> ret;
|
|
||||||
for (size_t i=DimRows-1; i--; )
|
|
||||||
for (size_t j=DimCols-1;j--; ret[i][j]=rows[i<row?i:i+1][j<col?j:j+1]);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
T cofactor(size_t row, size_t col) const {
|
|
||||||
return get_minor(row,col).det()*((row+col)%2 ? -1 : 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
mat<DimRows,DimCols,T> adjugate() const {
|
|
||||||
mat<DimRows,DimCols,T> ret;
|
|
||||||
for (size_t i=DimRows; i--; )
|
|
||||||
for (size_t j=DimCols; j--; ret[i][j]=cofactor(i,j));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
mat<DimRows,DimCols,T> invert_transpose() {
|
|
||||||
mat<DimRows,DimCols,T> ret = adjugate();
|
|
||||||
T tmp = ret[0]*rows[0];
|
|
||||||
return ret/tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
mat<DimRows,DimCols,T> invert() {
|
|
||||||
return invert_transpose().transpose();
|
|
||||||
}
|
|
||||||
|
|
||||||
mat<DimCols,DimRows,T> transpose() {
|
|
||||||
mat<DimCols,DimRows,T> ret;
|
|
||||||
for (size_t i=DimCols; i--; ret[i]=this->col(i));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
template<size_t DimRows,size_t DimCols,typename T> vec<DimRows,T> operator*(const mat<DimRows,DimCols,T>& lhs, const vec<DimCols,T>& rhs) {
|
|
||||||
vec<DimRows,T> ret;
|
|
||||||
for (size_t i=DimRows; i--; ret[i]=lhs[i]*rhs);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<size_t R1,size_t C1,size_t C2,typename T>mat<R1,C2,T> operator*(const mat<R1,C1,T>& lhs, const mat<C1,C2,T>& rhs) {
|
|
||||||
mat<R1,C2,T> result;
|
|
||||||
for (size_t i=R1; i--; )
|
|
||||||
for (size_t j=C2; j--; result[i][j]=lhs[i]*rhs.col(j));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<size_t DimRows,size_t DimCols,typename T>mat<DimCols,DimRows,T> operator/(mat<DimRows,DimCols,T> lhs, const T& rhs) {
|
|
||||||
for (size_t i=DimRows; i--; lhs[i]=lhs[i]/rhs);
|
|
||||||
return lhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
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__
|
#endif //__GEOMETRY_H__
|
||||||
|
|
||||||
|
54
src/external/tests/tinyrenderer/model.cpp
vendored
54
src/external/tests/tinyrenderer/model.cpp
vendored
@ -5,7 +5,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include "model.h"
|
#include "model.h"
|
||||||
|
|
||||||
Model::Model(const char *filename) : verts_(), faces_() {
|
Model::Model(const char *filename) : verts_(), faces_(), norms_(), uv_(), diffusemap_() {
|
||||||
std::ifstream in;
|
std::ifstream in;
|
||||||
in.open (filename, std::ifstream::in);
|
in.open (filename, std::ifstream::in);
|
||||||
if (in.fail()) return;
|
if (in.fail()) return;
|
||||||
@ -19,18 +19,29 @@ Model::Model(const char *filename) : verts_(), faces_() {
|
|||||||
Vec3f v;
|
Vec3f v;
|
||||||
for (int i=0;i<3;i++) iss >> v[i];
|
for (int i=0;i<3;i++) iss >> v[i];
|
||||||
verts_.push_back(v);
|
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 ")) {
|
} else if (!line.compare(0, 2, "f ")) {
|
||||||
std::vector<int> f;
|
std::vector<Vec3i> f;
|
||||||
int itrash, idx;
|
Vec3i tmp;
|
||||||
iss >> trash;
|
iss >> trash;
|
||||||
while (iss >> idx >> trash >> itrash >> trash >> itrash) {
|
while (iss >> tmp[0] >> trash >> tmp[1] >> trash >> tmp[2]) {
|
||||||
idx--; // in wavefront obj all indices start at 1, not zero
|
for (int i=0; i<3; i++) tmp[i]--; // in wavefront obj all indices start at 1, not zero
|
||||||
f.push_back(idx);
|
f.push_back(tmp);
|
||||||
}
|
}
|
||||||
faces_.push_back(f);
|
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() {
|
Model::~Model() {
|
||||||
@ -45,10 +56,37 @@ int Model::nfaces() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> Model::face(int idx) {
|
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) {
|
Vec3f Model::vert(int i) {
|
||||||
return verts_[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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
10
src/external/tests/tinyrenderer/model.h
vendored
10
src/external/tests/tinyrenderer/model.h
vendored
@ -3,17 +3,25 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "geometry.h"
|
#include "geometry.h"
|
||||||
|
#include "tgaimage.h"
|
||||||
|
|
||||||
class Model {
|
class Model {
|
||||||
private:
|
private:
|
||||||
std::vector<Vec3f> verts_;
|
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:
|
public:
|
||||||
Model(const char *filename);
|
Model(const char *filename);
|
||||||
~Model();
|
~Model();
|
||||||
int nverts();
|
int nverts();
|
||||||
int nfaces();
|
int nfaces();
|
||||||
|
Vec3f norm(int iface, int nvert);
|
||||||
Vec3f vert(int i);
|
Vec3f vert(int i);
|
||||||
|
Vec2i uv(int iface, int nvert);
|
||||||
|
TGAColor diffuse(Vec2i uv);
|
||||||
std::vector<int> face(int idx);
|
std::vector<int> face(int idx);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
357
src/external/tests/tinyrenderer/tgaimage.cpp
vendored
Normal file
357
src/external/tests/tinyrenderer/tgaimage.cpp
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
|
98
src/external/tests/tinyrenderer/tgaimage.h
vendored
Normal file
98
src/external/tests/tinyrenderer/tgaimage.h
vendored
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#ifndef __IMAGE_H__
|
||||||
|
#define __IMAGE_H__
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#pragma pack(push,1)
|
||||||
|
struct TGA_Header {
|
||||||
|
char idlength;
|
||||||
|
char colormaptype;
|
||||||
|
char datatypecode;
|
||||||
|
short colormaporigin;
|
||||||
|
short colormaplength;
|
||||||
|
char colormapdepth;
|
||||||
|
short x_origin;
|
||||||
|
short y_origin;
|
||||||
|
short width;
|
||||||
|
short height;
|
||||||
|
char bitsperpixel;
|
||||||
|
char imagedescriptor;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct TGAColor {
|
||||||
|
unsigned char bgra[4];
|
||||||
|
unsigned char bytespp;
|
||||||
|
|
||||||
|
TGAColor() : bgra(), bytespp(1) {
|
||||||
|
for (int i=0; i<4; i++) bgra[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) {
|
||||||
|
bgra[0] = B;
|
||||||
|
bgra[1] = G;
|
||||||
|
bgra[2] = R;
|
||||||
|
bgra[3] = A;
|
||||||
|
}
|
||||||
|
|
||||||
|
TGAColor(unsigned char v) : bgra(), bytespp(1) {
|
||||||
|
for (int i=0; i<4; i++) bgra[i] = 0;
|
||||||
|
bgra[0] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) {
|
||||||
|
for (int i=0; i<(int)bpp; i++) {
|
||||||
|
bgra[i] = p[i];
|
||||||
|
}
|
||||||
|
for (int i=bpp; i<4; i++) {
|
||||||
|
bgra[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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__
|
215
src/external/tests/tinyrenderer/tinyrenderer.cpp
vendored
Normal file
215
src/external/tests/tinyrenderer/tinyrenderer.cpp
vendored
Normal 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)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user