Start moving model around in 3d space

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

View File

@ -981,11 +981,19 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
DTRAsset_LoadBitmap(input->api, assetStack, 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);
} }
} }

View File

@ -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,10 +349,10 @@ 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);
i32 numVertexesParsed = 0; i32 numVertexesParsed = 0;
bool moreVertexesToParse = true; bool moreVertexesToParse = true;
while (moreVertexesToParse) while (moreVertexesToParse)
@ -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.

View File

@ -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)

View File

@ -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);

View File

@ -752,14 +752,6 @@ FILE_SCOPE void DebugRenderMarkers(DTRRenderBuffer *const renderBuffer, const Dq
} }
} }
FILE_SCOPE void SIMDTexturedTriangle(DTRRenderBuffer *const renderBuffer, const DqnV3 p1,
const DqnV3 p2, const DqnV3 p3, const DqnV2 uv1,
const DqnV2 uv2, const DqnV2 uv3,
const DTRBitmap *const texture, const DqnV4 color,
const DqnV2i min, const DqnV2i max)
{
DTR_DEBUG_EP_TIMED_FUNCTION();
#define DEBUG_SIMD_AUTO_CHOOSE_BEGIN_CYCLE_COUNT(type) \ #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);
@ -865,13 +890,18 @@ FILE_SCOPE void SIMDTexturedTriangle(DTRRenderBuffer *const renderBuffer, const
DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle_Preamble_SIMDStep); DEBUG_SIMD_AUTO_CHOOSE_END_CYCLE_COUNT(Triangle_Preamble_SIMDStep);
} }
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);
} }
} }

View File

@ -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

137
src/dqn.h
View File

@ -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);
@ -541,19 +542,23 @@ DQN_FILE_SCOPE f32 DqnV3_Dot (DqnV3 a, DqnV3 b);
DQN_FILE_SCOPE bool DqnV3_Equals (DqnV3 a, DqnV3 b); DQN_FILE_SCOPE 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, DqnV3 b) { return DqnV3_Hadamard(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, f32 b) { return DqnV3_Scalef (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, i32 b) { return DqnV3_Scalei (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, DqnV3 b) { return (a = DqnV3_Hadamard(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 (a = DqnV3_Scalef (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, i32 b) { return (a = DqnV3_Scalei (a, b)); } DQN_FILE_SCOPE inline DqnV3 &operator*=(DqnV3 &a, DqnV3 b) { return (a = DqnV3_Hadamard(a, b)); }
DQN_FILE_SCOPE inline DqnV3 &operator-=(DqnV3 &a, DqnV3 b) { return (a = DqnV3_Sub (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, DqnV3 b) { return (a = DqnV3_Add (a, b)); } DQN_FILE_SCOPE inline DqnV3 &operator*=(DqnV3 &a, i32 b) { return (a = DqnV3_Scalei (a, b)); }
DQN_FILE_SCOPE inline bool operator==(DqnV3 a, DqnV3 b) { return DqnV3_Equals (a, b); } DQN_FILE_SCOPE inline DqnV3 &operator-=(DqnV3 &a, DqnV3 b) { return (a = DqnV3_Sub (a, b)); }
DQN_FILE_SCOPE inline DqnV3 &operator+=(DqnV3 &a, DqnV3 b) { return (a = DqnV3_Add (a, b)); }
DQN_FILE_SCOPE inline bool operator==(DqnV3 a, DqnV3 b) { return DqnV3_Equals (a, b); }
// DqnV3i // DqnV3i
DQN_FILE_SCOPE DqnV3i DqnV3i_3i(i32 x, i32 y, i32 z); DQN_FILE_SCOPE DqnV3i DqnV3i_3i(i32 x, i32 y, i32 z);
@ -580,6 +585,7 @@ typedef union DqnV4 {
// Create a vector using ints and typecast to floats // 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,18 +614,20 @@ 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_Translate(f32 x, f32 y, f32 z); DQN_FILE_SCOPE DqnMat4 DqnMat4_Perspective (f32 fovYDegrees, f32 aspectRatio, f32 zNear, f32 zFar);
DQN_FILE_SCOPE DqnMat4 DqnMat4_Rotate (f32 radians, f32 x, f32 y, f32 z); DQN_FILE_SCOPE DqnMat4 DqnMat4_Translate (f32 x, f32 y, f32 z);
DQN_FILE_SCOPE DqnMat4 DqnMat4_Scale (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_Mul (DqnMat4 a, DqnMat4 b); DQN_FILE_SCOPE DqnMat4 DqnMat4_Scale (f32 x, f32 y, f32 z);
DQN_FILE_SCOPE DqnV4 DqnMat4_MulV4 (DqnMat4 a, DqnV4 b); DQN_FILE_SCOPE DqnMat4 DqnMat4_Mul (DqnMat4 a, DqnMat4 b);
DQN_FILE_SCOPE DqnV4 DqnMat4_MulV4 (DqnMat4 a, DqnV4 b);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Other Math // Other Math
@ -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};
@ -2281,7 +2318,7 @@ DQN_FILE_SCOPE bool DqnV4_Equals(DqnV4 a, DqnV4 b)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
DQN_FILE_SCOPE DqnMat4 DqnMat4_Identity() DQN_FILE_SCOPE DqnMat4 DqnMat4_Identity()
{ {
DqnMat4 result = {0}; DqnMat4 result = {0};
result.e[0][0] = 1; result.e[0][0] = 1;
result.e[1][1] = 1; result.e[1][1] = 1;
result.e[2][2] = 1; result.e[2][2] = 1;
@ -2289,13 +2326,13 @@ 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);
result.e[1][1] = +2.0f / (top - bottom); result.e[1][1] = +2.0f / (top - bottom);
result.e[2][2] = -2.0f / (zFar - zNear); result.e[2][2] = -2.0f / (zFar - zNear);
result.e[3][0] = -(right + left) / (right - left); result.e[3][0] = -(right + left) / (right - left);
result.e[3][1] = -(top + bottom) / (top - bottom); result.e[3][1] = -(top + bottom) / (top - bottom);
@ -2304,6 +2341,24 @@ DqnMat4_Ortho(f32 left, f32 right, f32 bottom, f32 top, f32 zNear, f32 zFar)
return result; 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;
} }

View File

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

View File

@ -1,221 +1,75 @@
#ifndef __GEOMETRY_H__ #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__

View File

@ -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, 2, "f ")) { } else if (!line.compare(0, 3, "vn ")) {
std::vector<int> f; iss >> trash >> trash;
int itrash, idx; Vec3f n;
for (int i=0;i<3;i++) iss >> n[i];
norms_.push_back(n);
} else if (!line.compare(0, 3, "vt ")) {
iss >> trash >> trash;
Vec2f uv;
for (int i=0;i<2;i++) iss >> uv[i];
uv_.push_back(uv);
} else if (!line.compare(0, 2, "f ")) {
std::vector<Vec3i> f;
Vec3i tmp;
iss >> trash; 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();
}

View File

@ -3,18 +3,26 @@
#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 vert(int i); Vec3f norm(int iface, int nvert);
std::vector<int> face(int idx); Vec3f vert(int i);
Vec2i uv(int iface, int nvert);
TGAColor diffuse(Vec2i uv);
std::vector<int> face(int idx);
}; };
#endif //__MODEL_H__ #endif //__MODEL_H__

View File

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

View File

@ -0,0 +1,98 @@
#ifndef __IMAGE_H__
#define __IMAGE_H__
#include <fstream>
#pragma pack(push,1)
struct TGA_Header {
char idlength;
char colormaptype;
char datatypecode;
short colormaporigin;
short colormaplength;
char colormapdepth;
short x_origin;
short y_origin;
short width;
short height;
char bitsperpixel;
char imagedescriptor;
};
#pragma pack(pop)
struct TGAColor {
unsigned char bgra[4];
unsigned char bytespp;
TGAColor() : bgra(), bytespp(1) {
for (int i=0; i<4; i++) bgra[i] = 0;
}
TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) {
bgra[0] = B;
bgra[1] = G;
bgra[2] = R;
bgra[3] = A;
}
TGAColor(unsigned char v) : bgra(), bytespp(1) {
for (int i=0; i<4; i++) bgra[i] = 0;
bgra[0] = v;
}
TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) {
for (int i=0; i<(int)bpp; i++) {
bgra[i] = p[i];
}
for (int i=bpp; i<4; i++) {
bgra[i] = 0;
}
}
TGAColor operator *(float intensity) const {
TGAColor res = *this;
intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity));
for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity;
return res;
}
};
class TGAImage {
protected:
unsigned char* data;
int width;
int height;
int bytespp;
bool load_rle_data(std::ifstream &in);
bool unload_rle_data(std::ofstream &out);
public:
enum Format {
GRAYSCALE=1, RGB=3, RGBA=4
};
TGAImage();
TGAImage(int w, int h, int bpp);
TGAImage(const TGAImage &img);
bool read_tga_file(const char *filename);
bool write_tga_file(const char *filename, bool rle=true);
bool flip_horizontally();
bool flip_vertically();
bool scale(int w, int h);
TGAColor get(int x, int y);
bool set(int x, int y, TGAColor &c);
bool set(int x, int y, const TGAColor &c);
~TGAImage();
TGAImage & operator =(const TGAImage &img);
int get_width();
int get_height();
int get_bytespp();
unsigned char *buffer();
void clear();
};
#endif //__IMAGE_H__

View File

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