Fix zbuffer bug clipping at poly's behind 0, 0
Mistakenly use FLT_MIN as the largest negative number possible, where we should be using -FLT_MAX.
This commit is contained in:
		
							parent
							
								
									a03de5de80
								
							
						
					
					
						commit
						4eb54c09b1
					
				| @ -6,17 +6,60 @@ | ||||
| #define STB_IMAGE_IMPLEMENTATION | ||||
| #include "external/stb_image.h" | ||||
| 
 | ||||
| // #define DTR_DEBUG_RENDER_FONT_BITMAP
 | ||||
| #ifdef DTR_DEBUG_RENDER_FONT_BITMAP | ||||
| 	#define STB_IMAGE_WRITE_IMPLEMENTATION | ||||
| 	#include "external/tests/stb_image_write.h" | ||||
| #endif | ||||
| 
 | ||||
| #define DQN_IMPLEMENTATION | ||||
| #include "dqn.h" | ||||
| 
 | ||||
| #include "external/tests/tinyrenderer/geometry.h" | ||||
| #include "external/tests/tinyrenderer/model.cpp" | ||||
| #include <algorithm> | ||||
| #include <vector> | ||||
| 
 | ||||
| FILE_SCOPE void DebugTestWavefrontFaceAndVertexParser(WavefrontObj *const obj) | ||||
| { | ||||
| 	if (!obj) DQN_ASSERT(DQN_INVALID_CODE_PATH); | ||||
| 	Model model = Model("african_head.obj"); | ||||
| 
 | ||||
| 	DQN_ASSERT(obj->model.faces.count == model.nfaces()); | ||||
| 	for (i32 i = 0; i < model.nfaces(); i++) | ||||
| 	{ | ||||
| 		std::vector<i32> correctFace = model.face(i); | ||||
| 		WavefrontModelFace *myFace   = &obj->model.faces.data[i]; | ||||
| 
 | ||||
| 		DQN_ASSERT(myFace->vertexIndexArray.count == correctFace.size()); | ||||
| 
 | ||||
| 		for (i32 j = 0; j < myFace->vertexIndexArray.count; j++) | ||||
| 		{ | ||||
| 			// Ensure the vertex index references are correct per face
 | ||||
| 			DQN_ASSERT(myFace->vertexIndexArray.data[j] == correctFace[j]); | ||||
| 
 | ||||
| 			Vec3f tmp           = model.vert(correctFace[j]); | ||||
| 			DqnV3 correctVertex = DqnV3_3f(tmp[0], tmp[1], tmp[2]); | ||||
| 			DqnV3 myVertex      = (obj->geometryArray.data[myFace->vertexIndexArray.data[j]]).xyz; | ||||
| 
 | ||||
| 			// Ensure the vertex values read are correct
 | ||||
| 			for (i32 k = 0; k < DQN_ARRAY_COUNT(correctVertex.e); k++) | ||||
| 			{ | ||||
| 				f32 delta = DQN_ABS(correctVertex.e[k] - myVertex.e[k]); | ||||
| 				DQN_ASSERT(delta < 0.1f); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #include <math.h> | ||||
| 
 | ||||
| FILE_SCOPE inline WavefrontModelFace ObjWavefrontModelFaceInit(i32 capacity = 3) | ||||
| { | ||||
| 	WavefrontModelFace result = {}; | ||||
| 	DQN_ASSERT(DqnArray_Init(&result.vertexArray, capacity)); | ||||
| 	DQN_ASSERT(DqnArray_Init(&result.textureArray, capacity)); | ||||
| 	DQN_ASSERT(DqnArray_Init(&result.normalArray, capacity)); | ||||
| 	DQN_ASSERT(DqnArray_Init(&result.vertexIndexArray, capacity)); | ||||
| 	DQN_ASSERT(DqnArray_Init(&result.textureIndexArray, capacity)); | ||||
| 	DQN_ASSERT(DqnArray_Init(&result.normalIndexArray, capacity)); | ||||
| 
 | ||||
| 	return result; | ||||
| } | ||||
| @ -28,14 +71,14 @@ FILE_SCOPE bool ObjWaveFrontInit(WavefrontObj *const obj, const i32 vertexInitCa | ||||
| 	bool initialised = false; | ||||
| 
 | ||||
| 	initialised |= DqnArray_Init(&obj->geometryArray, vertexInitCapacity); | ||||
| 	initialised |= DqnArray_Init(&obj->texUVArray,    vertexInitCapacity); | ||||
| 	initialised |= DqnArray_Init(&obj->textureArray,    vertexInitCapacity); | ||||
| 	initialised |= DqnArray_Init(&obj->normalArray,   vertexInitCapacity); | ||||
| 	initialised |= DqnArray_Init(&obj->model.faces,   faceInitCapacity); | ||||
| 
 | ||||
| 	if (!initialised) | ||||
| 	{ | ||||
| 		DqnArray_Free(&obj->geometryArray); | ||||
| 		DqnArray_Free(&obj->texUVArray); | ||||
| 		DqnArray_Free(&obj->textureArray); | ||||
| 		DqnArray_Free(&obj->normalArray); | ||||
| 		DqnArray_Free(&obj->model.faces); | ||||
| 	} | ||||
| @ -49,7 +92,7 @@ FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const me | ||||
| 	if (!memory || !path || !obj) return false; | ||||
| 
 | ||||
| 	PlatformFile file = {}; | ||||
| 	if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read)) | ||||
| 	if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read, PlatformFileAction_OpenOnly)) | ||||
| 		return false; // TODO(doyle): Logging
 | ||||
| 
 | ||||
| 	// TODO(doyle): Make arrays use given memory not malloc
 | ||||
| @ -120,8 +163,8 @@ FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const me | ||||
| 						scan++; | ||||
| 					} | ||||
| 
 | ||||
| 					i32 f32Len = (i32)((size_t)scan - (size_t)f32StartPtr); | ||||
| 					v4.e[vIndex++] = (f32)atof(f32StartPtr); // Dqn_StrToF32(f32StartPtr, f32Len);
 | ||||
| 					i32 f32Len     = (i32)((size_t)scan - (size_t)f32StartPtr); | ||||
| 					v4.e[vIndex++] = Dqn_StrToF32(f32StartPtr, f32Len); | ||||
| 					DQN_ASSERT(vIndex < DQN_ARRAY_COUNT(v4.e)); | ||||
| 
 | ||||
| 					while (scan && (*scan == ' ' || *scan == '\n')) scan++; | ||||
| @ -137,7 +180,7 @@ FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const me | ||||
| 				} | ||||
| 				else if (type == WavefrontVertexType_Texture) | ||||
| 				{ | ||||
| 					DqnArray_Push(&obj->texUVArray, v4.xyz); | ||||
| 					DqnArray_Push(&obj->textureArray, v4.xyz); | ||||
| 				} | ||||
| 				else if (type == WavefrontVertexType_Normal) | ||||
| 				{ | ||||
| @ -214,19 +257,19 @@ FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const me | ||||
| 						{ | ||||
| 							// NOTE(doyle): Obj format starts indexing from 1,
 | ||||
| 							// so offset by -1 to make it zero-based indexes.
 | ||||
| 							i32 index = (i32)Dqn_StrToI64(numStartPtr, numLen) - 1; | ||||
| 							i32 vertIndex = (i32)Dqn_StrToI64(numStartPtr, numLen) - 1; | ||||
| 
 | ||||
| 							if (type == WavefrontVertexType_Geometric) | ||||
| 							{ | ||||
| 								DQN_ASSERT(DqnArray_Push(&face.vertexArray, index)); | ||||
| 								DQN_ASSERT(DqnArray_Push(&face.vertexIndexArray, vertIndex)); | ||||
| 							} | ||||
| 							else if (type == WavefrontVertexType_Texture) | ||||
| 							{ | ||||
| 								DQN_ASSERT(DqnArray_Push(&face.textureArray, index)); | ||||
| 								DQN_ASSERT(DqnArray_Push(&face.textureIndexArray, vertIndex)); | ||||
| 							} | ||||
| 							else if (type == WavefrontVertexType_Normal) | ||||
| 							{ | ||||
| 								DQN_ASSERT(DqnArray_Push(&face.normalArray, index)); | ||||
| 								DQN_ASSERT(DqnArray_Push(&face.normalIndexArray, vertIndex)); | ||||
| 							} | ||||
| 						} | ||||
| 
 | ||||
| @ -248,11 +291,6 @@ FILE_SCOPE bool ObjWavefrontLoad(const PlatformAPI api, PlatformMemory *const me | ||||
| 							moreVertexesToParse = false; | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 					if (obj->model.faces.count == 7470 || obj->model.faces.count == 2491) | ||||
| 					{ | ||||
| 						int x = 5; | ||||
| 					} | ||||
| 				} | ||||
| 				DQN_ASSERT(numVertexesParsed >= 3); | ||||
| 				DQN_ASSERT(DqnArray_Push(&obj->model.faces, face)); | ||||
| @ -368,7 +406,7 @@ FILE_SCOPE bool BitmapFontCreate(const PlatformAPI api, | ||||
| 	// Load font data
 | ||||
| 	////////////////////////////////////////////////////////////////////////////
 | ||||
| 	PlatformFile file = {}; | ||||
| 	if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read)) | ||||
| 	if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read, PlatformFileAction_OpenOnly)) | ||||
| 		return false; // TODO(doyle): Logging
 | ||||
| 
 | ||||
| 	DqnTempMemStack tmpMemRegion = DqnMemStack_BeginTempRegion(&memory->transMemStack); | ||||
| @ -451,6 +489,7 @@ FILE_SCOPE bool BitmapFontCreate(const PlatformAPI api, | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| // TODO(doyle): Uses malloc
 | ||||
| FILE_SCOPE bool BitmapLoad(const PlatformAPI api, DTRBitmap *bitmap, | ||||
|                            const char *const path, | ||||
|                            DqnMemStack *const transMemStack) | ||||
| @ -458,7 +497,7 @@ FILE_SCOPE bool BitmapLoad(const PlatformAPI api, DTRBitmap *bitmap, | ||||
| 	if (!bitmap) return false; | ||||
| 
 | ||||
| 	PlatformFile file = {}; | ||||
| 	if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read)) | ||||
| 	if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read, PlatformFileAction_OpenOnly)) | ||||
| 		return false; | ||||
| 
 | ||||
| 	DqnTempMemStack tempBuffer = DqnMemStack_BeginTempRegion(transMemStack); | ||||
| @ -474,8 +513,10 @@ FILE_SCOPE bool BitmapLoad(const PlatformAPI api, DTRBitmap *bitmap, | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		const u32 FORCE_4_BPP = 4; | ||||
| 		bitmap->memory = stbi_load_from_memory(rawData, (i32)file.size, &bitmap->dim.w, | ||||
| 		                                       &bitmap->dim.h, &bitmap->bytesPerPixel, 4); | ||||
| 		                                       &bitmap->dim.h, NULL, FORCE_4_BPP); | ||||
| 		bitmap->bytesPerPixel = FORCE_4_BPP; | ||||
| 	} | ||||
| 	DqnMemStack_EndTempRegion(tempBuffer); | ||||
| 	if (!bitmap->memory) return false; | ||||
| @ -495,14 +536,15 @@ FILE_SCOPE bool BitmapLoad(const PlatformAPI api, DTRBitmap *bitmap, | ||||
| 			color.g     = (f32)((pixel >> 8) & 0xFF); | ||||
| 			color.r     = (f32)((pixel >> 0) & 0xFF); | ||||
| 
 | ||||
| 			color *= DTRRENDER_INV_255; | ||||
| 			color = DTRRender_PreMultiplyAlphaSRGB1WithLinearConversion(color); | ||||
| 			color *= 255.0f; | ||||
| 			DqnV4 preMulColor = color; | ||||
| 			preMulColor *= DTRRENDER_INV_255; | ||||
| 			preMulColor = DTRRender_PreMultiplyAlphaSRGB1WithLinearConversion(preMulColor); | ||||
| 			preMulColor *= 255.0f; | ||||
| 
 | ||||
| 			pixel = (((u32)color.a << 24) | | ||||
| 			         ((u32)color.b << 16) | | ||||
| 			         ((u32)color.g << 8) | | ||||
| 			         ((u32)color.r << 0)); | ||||
| 			pixel = (((u32)preMulColor.a << 24) | | ||||
| 			         ((u32)preMulColor.b << 16) | | ||||
| 			         ((u32)preMulColor.g << 8) | | ||||
| 			         ((u32)preMulColor.r << 0)); | ||||
| 
 | ||||
| 			pixelPtr[x] = pixel; | ||||
| 		} | ||||
| @ -1370,7 +1412,7 @@ void CompAssignment(DTRRenderBuffer *const renderBuffer, PlatformInput *const in | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| FILE_SCOPE void TestStrToF32Converter() | ||||
| FILE_SCOPE void DebugTestStrToF32Converter() | ||||
| { | ||||
| 	const f32 EPSILON = 0.001f; | ||||
| 	const char a[]    = "-0.66248"; | ||||
| @ -1459,7 +1501,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer, | ||||
| 	DTR_DEBUG_EP_TIMED_FUNCTION(); | ||||
| 	if (!memory->isInit) | ||||
| 	{ | ||||
| 		TestStrToF32Converter(); | ||||
| 		DebugTestStrToF32Converter(); | ||||
| 		DTR_DEBUG_EP_TIMED_BLOCK("DTR_Update Memory Initialisation"); | ||||
| 		// NOTE(doyle): Do premultiply ourselves
 | ||||
| 		stbi_set_unpremultiply_on_load(true); | ||||
| @ -1482,7 +1524,11 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer, | ||||
| 		           &memory->transMemStack); | ||||
| 		DqnMemStack_EndTempRegion(tmp); | ||||
| 
 | ||||
| 		DQN_ASSERT(BitmapLoad(input->api, &state->objTex, "african_head_diffuse.tga", | ||||
| 		                      &memory->transMemStack)); | ||||
| 		DQN_ASSERT(ObjWavefrontLoad(input->api, memory, "african_head.obj", &state->obj)); | ||||
| 
 | ||||
| 		DebugTestWavefrontFaceAndVertexParser(&state->obj); | ||||
| 	} | ||||
| 
 | ||||
| 	DqnTempMemStack transMemTmpRegion = DqnMemStack_BeginTempRegion(&memory->transMemStack); | ||||
| @ -1502,7 +1548,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer, | ||||
| 	////////////////////////////////////////////////////////////////////////////
 | ||||
| 	// Update and Render
 | ||||
| 	////////////////////////////////////////////////////////////////////////////
 | ||||
| 	DTRRender_Clear(&renderBuffer, DqnV3_3f(0.0f, 0.0f, 0.0f)); | ||||
| 	DTRRender_Clear(&renderBuffer, DqnV3_3f(0.5f, 0.0f, 1.0f)); | ||||
| 
 | ||||
| #if 1 | ||||
| 	DqnV4 colorRed    = DqnV4_4f(0.8f, 0, 0, 1); | ||||
| @ -1543,54 +1589,81 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer, | ||||
| 
 | ||||
| 		for (i32 i = 0; i < waveObj->model.faces.count; i++) | ||||
| 		{ | ||||
| 			if (i == 852) | ||||
| 			{ | ||||
| 				int BreakHere = 0; | ||||
| 			} | ||||
| 			WavefrontModelFace face = waveObj->model.faces.data[i]; | ||||
| 			DQN_ASSERT(face.vertexArray.count == 3); | ||||
| 			i32 vertAIndex = face.vertexArray.data[0]; | ||||
| 			i32 vertBIndex = face.vertexArray.data[1]; | ||||
| 			i32 vertCIndex = face.vertexArray.data[2]; | ||||
| 			DQN_ASSERT(face.vertexIndexArray.count == 3); | ||||
| 			i32 vertAIndex = face.vertexIndexArray.data[0]; | ||||
| 			i32 vertBIndex = face.vertexIndexArray.data[1]; | ||||
| 			i32 vertCIndex = face.vertexIndexArray.data[2]; | ||||
| 
 | ||||
| 			DqnV4 vertA = waveObj->geometryArray.data[vertAIndex]; | ||||
| 			DqnV4 vertB = waveObj->geometryArray.data[vertBIndex]; | ||||
| 			DqnV4 vertC = waveObj->geometryArray.data[vertCIndex]; | ||||
| 			DQN_ASSERT(vertAIndex < waveObj->geometryArray.count); | ||||
| 			DQN_ASSERT(vertBIndex < waveObj->geometryArray.count); | ||||
| 			DQN_ASSERT(vertCIndex < waveObj->geometryArray.count); | ||||
| 
 | ||||
| 			DqnV4 vertAB = vertB - vertA; | ||||
| 			DqnV4 vertAC = vertC - vertA; | ||||
| 			DqnV3 normal = DqnV3_Cross(vertAC.xyz, vertAB.xyz); | ||||
| 
 | ||||
| 			f32 intensity = DqnV3_Dot(DqnV3_Normalise(normal), LIGHT); | ||||
| 			if (intensity > 0) | ||||
| 			if (intensity < 0) continue; | ||||
| 			DqnV4 modelCol = DqnV4_4f(1, 1, 1, 1); | ||||
| 			modelCol.rgb *= DQN_ABS(intensity); | ||||
| 
 | ||||
| 			DqnV3 screenVA = (vertA.xyz * MODEL_SCALE) + modelP; | ||||
| 			DqnV3 screenVB = (vertB.xyz * MODEL_SCALE) + modelP; | ||||
| 			DqnV3 screenVC = (vertC.xyz * MODEL_SCALE) + modelP; | ||||
| 
 | ||||
| 			// TODO(doyle): Why do we need rounding here? Maybe it's because
 | ||||
| 			// I don't do any interpolation in the triangle routine for jagged
 | ||||
| 			// edges.
 | ||||
| 			screenVA.x = (f32)(i32)(screenVA.x + 0.5f); | ||||
| 			screenVA.y = (f32)(i32)(screenVA.y + 0.5f); | ||||
| 			screenVB.x = (f32)(i32)(screenVB.x + 0.5f); | ||||
| 			screenVB.y = (f32)(i32)(screenVB.y + 0.5f); | ||||
| 			screenVC.x = (f32)(i32)(screenVC.x + 0.5f); | ||||
| 			screenVC.y = (f32)(i32)(screenVC.y + 0.5f); | ||||
| 
 | ||||
| 			i32 textureAIndex = face.textureIndexArray.data[0]; | ||||
| 			i32 textureBIndex = face.textureIndexArray.data[1]; | ||||
| 			i32 textureCIndex = face.textureIndexArray.data[2]; | ||||
| 
 | ||||
| 			DqnV2 texA = waveObj->textureArray.data[textureAIndex].xy; | ||||
| 			DqnV2 texB = waveObj->textureArray.data[textureBIndex].xy; | ||||
| 			DqnV2 texC = waveObj->textureArray.data[textureCIndex].xy; | ||||
| 			DQN_ASSERT(textureAIndex < waveObj->textureArray.count); | ||||
| 			DQN_ASSERT(textureBIndex < waveObj->textureArray.count); | ||||
| 			DQN_ASSERT(textureCIndex < waveObj->textureArray.count); | ||||
| 
 | ||||
| 			bool DEBUG_SIMPLE_MODE = false; | ||||
| 			if (DTR_DEBUG && DEBUG_SIMPLE_MODE) | ||||
| 			{ | ||||
| 				DqnV4 modelCol = DqnV4_4f(1, 1, 1, 1); | ||||
| 				modelCol.rgb *= intensity; | ||||
| 
 | ||||
| 				DqnV3 screenVA = (vertA.xyz * MODEL_SCALE) + modelP; | ||||
| 				DqnV3 screenVB = (vertB.xyz * MODEL_SCALE) + modelP; | ||||
| 				DqnV3 screenVC = (vertC.xyz * MODEL_SCALE) + modelP; | ||||
| 
 | ||||
| 				// TODO(doyle): Why do we need rounding here? Maybe it's because
 | ||||
| 				// I don't do any interpolation in the triangle routine for jagged
 | ||||
| 				// edges.
 | ||||
| 				screenVA.x = (f32)(i32)(screenVA.x + 0.5f); | ||||
| 				screenVA.y = (f32)(i32)(screenVA.y + 0.5f); | ||||
| 				screenVB.x = (f32)(i32)(screenVB.x + 0.5f); | ||||
| 				screenVB.y = (f32)(i32)(screenVB.y + 0.5f); | ||||
| 				screenVC.x = (f32)(i32)(screenVC.x + 0.5f); | ||||
| 				screenVC.y = (f32)(i32)(screenVC.y + 0.5f); | ||||
| 
 | ||||
| 				DTRRender_Triangle(&renderBuffer, screenVA, screenVB, screenVC, modelCol); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				DTRRender_TexturedTriangle(&renderBuffer, screenVA, screenVB, screenVC, texA, texB, | ||||
| 				                           texC, &state->objTex, modelCol); | ||||
| 			} | ||||
| 
 | ||||
| #if 0 | ||||
| 				DqnV4 wireColor = DqnV4_1f(1.0f); | ||||
| 				for (i32 j = 0; j < 3; j++) | ||||
| 				{ | ||||
| 					DTRRender_Line(renderBuffer, DqnV2i_V2(screenVertA), DqnV2i_V2(screenVertB), wireColor); | ||||
| 				} | ||||
| #endif | ||||
| 			bool DEBUG_WIREFRAME = false; | ||||
| 			if (DTR_DEBUG && DEBUG_WIREFRAME) | ||||
| 			{ | ||||
| 				DqnV4 wireColor = DqnV4_4f(1.0f, 1.0f, 1.0f, 0.01f); | ||||
| 				DTRRender_Line(&renderBuffer, DqnV2i_V2(screenVA.xy), DqnV2i_V2(screenVB.xy), wireColor); | ||||
| 				DTRRender_Line(&renderBuffer, DqnV2i_V2(screenVB.xy), DqnV2i_V2(screenVC.xy), wireColor); | ||||
| 				DTRRender_Line(&renderBuffer, DqnV2i_V2(screenVC.xy), DqnV2i_V2(screenVA.xy), wireColor); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Rect drawing
 | ||||
| 	if (0) | ||||
| 	{ | ||||
| 		DTRRenderTransform transform = DTRRender_DefaultTransform(); | ||||
| 		transform.rotation           = rotation + 45; | ||||
| @ -1600,6 +1673,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer, | ||||
| 	} | ||||
| 
 | ||||
| 	// Bitmap drawing
 | ||||
| 	if (0) | ||||
| 	{ | ||||
| 		DTRRenderTransform transform = DTRRender_DefaultTransform(); | ||||
| 		transform.scale              = DqnV2_1f(2.0f); | ||||
| @ -1618,11 +1692,12 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer, | ||||
| #endif | ||||
| 
 | ||||
| 	DTRDebug_Update(state, &renderBuffer, input, memory); | ||||
| 
 | ||||
| 	////////////////////////////////////////////////////////////////////////////
 | ||||
| 	// End Update
 | ||||
| 	////////////////////////////////////////////////////////////////////////////
 | ||||
| 	DqnMemStack_EndTempRegion(transMemTmpRegion); | ||||
| 	DqnMemStack_ClearCurrBlock(&memory->transMemStack, true); | ||||
| 
 | ||||
| 	DQN_ASSERT(memory->transMemStack.tempStackCount == 0); | ||||
| 	DQN_ASSERT(memory->permMemStack.tempStackCount == 0); | ||||
| } | ||||
|  | ||||
| @ -10,9 +10,9 @@ typedef void DTR_UpdateFunction(struct PlatformRenderBuffer *const renderBuffer, | ||||
| 
 | ||||
| typedef struct WavefrontModelFace | ||||
| { | ||||
| 	DqnArray<i32> vertexArray; | ||||
| 	DqnArray<i32> textureArray; | ||||
| 	DqnArray<i32> normalArray; | ||||
| 	DqnArray<i32> vertexIndexArray; | ||||
| 	DqnArray<i32> textureIndexArray; | ||||
| 	DqnArray<i32> normalIndexArray; | ||||
| } WavefrontModelFace; | ||||
| 
 | ||||
| typedef struct WavefrontModel | ||||
| @ -28,7 +28,7 @@ typedef struct WavefrontModel | ||||
| typedef struct WavefrontObj | ||||
| { | ||||
| 	DqnArray<DqnV4> geometryArray; | ||||
| 	DqnArray<DqnV3> texUVArray; | ||||
| 	DqnArray<DqnV3> textureArray; | ||||
| 	DqnArray<DqnV3> normalArray; | ||||
| 
 | ||||
| 	WavefrontModel model; | ||||
| @ -53,8 +53,9 @@ typedef struct DTRBitmap | ||||
| 
 | ||||
| typedef struct DTRState | ||||
| { | ||||
| 	DTRFont           font; | ||||
| 	DTRBitmap         bitmap; | ||||
| 	DTRFont      font; | ||||
| 	DTRBitmap    bitmap; | ||||
| 	WavefrontObj obj; | ||||
| 	DTRBitmap    objTex; | ||||
| } DTRState; | ||||
| #endif | ||||
|  | ||||
| @ -3,8 +3,56 @@ | ||||
| #include "DTRendererPlatform.h" | ||||
| #include "DTRendererRender.h" | ||||
| 
 | ||||
| #include "dqn.h" | ||||
| 
 | ||||
| DTRDebug globalDebug; | ||||
| 
 | ||||
| void DTRDebug_DumpZBuffer(DTRRenderBuffer *const renderBuffer, DqnMemStack *const transMemStack) | ||||
| { | ||||
| 	if (DTR_DEBUG) | ||||
| 	{ | ||||
| 		PlatformAPI *const api = &globalDebug.input->api; | ||||
| 		PlatformFile file      = {}; | ||||
| 
 | ||||
| 		u32 permissions   = (PlatformFilePermissionFlag_Read | PlatformFilePermissionFlag_Write); | ||||
| 		if (!api->FileOpen("zBufferDump.txt", &file, permissions, | ||||
| 		                   PlatformFileAction_CreateIfNotExist)) | ||||
| 		{ | ||||
| 			if (!api->FileOpen("zBufferDump.txt", &file, permissions, | ||||
| 			                   PlatformFileAction_ClearIfExist)) | ||||
| 			{ | ||||
| 				DQN_ASSERT(DQN_INVALID_CODE_PATH); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		DqnTempMemStack tmpMemRegion = DqnMemStack_BeginTempRegion(transMemStack); | ||||
| 
 | ||||
| 		size_t bufSize = DQN_MEGABYTE(16); | ||||
| 		char *bufString = (char *)DqnMemStack_Push(transMemStack, bufSize); | ||||
| 		char *bufPtr    = bufString; | ||||
| 		for (i32 i = 0; i < renderBuffer->width * renderBuffer->height; i++) | ||||
| 		{ | ||||
| 
 | ||||
| 			f32 zValue = renderBuffer->zBuffer[i]; | ||||
| 			if (zValue == DQN_F32_MIN) continue; | ||||
| 			i32 chWritten = Dqn_sprintf(bufPtr, "index %06d: %05.5f\n", i, zValue); | ||||
| 			if ((bufPtr + chWritten) > (bufString + bufSize)) | ||||
| 			{ | ||||
| 				size_t bufPtrAddr    = (size_t)(bufPtr + chWritten); | ||||
| 				size_t bufStringAddr = (size_t)(bufString + bufSize); | ||||
| 				DQN_ASSERT(DQN_INVALID_CODE_PATH); | ||||
| 			} | ||||
| 			bufPtr += chWritten; | ||||
| 		} | ||||
| 
 | ||||
| 		size_t writeSize    = (size_t)bufPtr - (size_t)bufString; | ||||
| 		size_t bytesWritten = api->FileWrite(&file, (u8 *)bufString, writeSize); | ||||
| 		api->FileClose(&file); | ||||
| 
 | ||||
| 		DqnMemStack_EndTempRegion(tmpMemRegion); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void DTRDebug_PushText(const char *const formatStr, ...) | ||||
| { | ||||
| 	if (DTR_DEBUG) | ||||
| @ -105,6 +153,21 @@ void DTRDebug_Update(DTRState *const state, | ||||
| 		debug->totalSetPixels += debug->counter[DTRDebugCounter_SetPixels]; | ||||
| 		debug->totalSetPixels = DQN_MAX(0, debug->totalSetPixels); | ||||
| 
 | ||||
| 		// memory
 | ||||
| 		{ | ||||
| 			PushMemStackText("PermBuffer", &memory->permMemStack); | ||||
| 			PushMemStackText("TransBuffer", &memory->transMemStack); | ||||
| 		} | ||||
| 
 | ||||
| 		DTRDebug_PushText("Mouse: %d, %d", input->mouse.x, input->mouse.y); | ||||
| 		DTRDebug_PushText("MouseLBtn: %s", (input->mouse.leftBtn.endedDown) ? "true" : "false"); | ||||
| 		DTRDebug_PushText("MouseRBtn: %s", (input->mouse.rightBtn.endedDown) ? "true" : "false"); | ||||
| 		DTRDebug_PushText(""); | ||||
| 
 | ||||
| 		DTRDebug_PushText("SSE2Support: %s", (input->canUseSSE2) ? "true" : "false"); | ||||
| 		DTRDebug_PushText("RDTSCSupport: %s", (input->canUseRdtsc) ? "true" : "false"); | ||||
| 		DTRDebug_PushText(""); | ||||
| 
 | ||||
| 		DTRDebug_PushText("TotalSetPixels: %'lld",    debug->totalSetPixels); | ||||
| 		DTRDebug_PushText("SetPixelsPerFrame: %'lld", debug->counter[DTRDebugCounter_SetPixels]); | ||||
| 		DTRDebug_PushText("TrianglesRendered: %'lld", debug->counter[DTRDebugCounter_RenderTriangle]); | ||||
| @ -116,15 +179,10 @@ void DTRDebug_Update(DTRState *const state, | ||||
| 		} | ||||
| 		DTRDebug_PushText(""); | ||||
| 
 | ||||
| 		// memory
 | ||||
| 		{ | ||||
| 			PushMemStackText("PermBuffer", &memory->permMemStack); | ||||
| 			PushMemStackText("TransBuffer", &memory->transMemStack); | ||||
| 		} | ||||
| 
 | ||||
| 		DTRDebug_PushText("SSE2Support: %s", (input->canUseSSE2) ? "true" : "false"); | ||||
| 		DTRDebug_PushText("SSE2Support: %s", (input->canUseRdtsc) ? "true" : "false"); | ||||
| 
 | ||||
| 		////////////////////////////////////////////////////////////////////////
 | ||||
| 		// End Debug Update
 | ||||
| 		////////////////////////////////////////////////////////////////////////
 | ||||
| 		debug->displayP = | ||||
| 			DqnV2_2i(0, debug->renderBuffer->height + globalDebug.displayYOffset); | ||||
| 
 | ||||
|  | ||||
| @ -4,7 +4,7 @@ | ||||
| #include "dqn.h" | ||||
| #define DTR_DEBUG 1 | ||||
| #if DTR_DEBUG | ||||
| 	#define DTR_DEBUG_RENDER 0 | ||||
| 	#define DTR_DEBUG_RENDER 1 | ||||
| 
 | ||||
| 	#define DTR_DEBUG_PROFILING_EASY_PROFILER 0 | ||||
| 	#if DTR_DEBUG_PROFILING_EASY_PROFILER | ||||
| @ -67,6 +67,7 @@ typedef struct DTRDebug | ||||
| 
 | ||||
| extern DTRDebug globalDebug; | ||||
| 
 | ||||
| void DTRDebug_DumpZBuffer(DTRRenderBuffer *const renderBuffer, DqnMemStack *const transMemStack); | ||||
| void DTRDebug_PushText(const char *const formatStr, ...); | ||||
| void DTRDebug_Update(DTRState *const state, | ||||
|                      DTRRenderBuffer *const renderBuffer, | ||||
|  | ||||
| @ -10,6 +10,13 @@ enum PlatformFilePermissionFlag | ||||
| 	PlatformFilePermissionFlag_Write = (1 << 1), | ||||
| }; | ||||
| 
 | ||||
| enum PlatformFileAction | ||||
| { | ||||
| 	PlatformFileAction_OpenOnly, | ||||
| 	PlatformFileAction_CreateIfNotExist, | ||||
| 	PlatformFileAction_ClearIfExist, | ||||
| }; | ||||
| 
 | ||||
| typedef struct PlatformFile | ||||
| { | ||||
| 	void   *handle; | ||||
| @ -17,16 +24,16 @@ typedef struct PlatformFile | ||||
| 	u32     permissionFlags; | ||||
| } PlatformFile; | ||||
| 
 | ||||
| typedef bool   PlatformAPI_FileOpen (const char *const path, PlatformFile *const file, | ||||
|                                      const u32 permissionFlags); | ||||
| typedef size_t PlatformAPI_FileRead (PlatformFile *const file, u8 *const buf, | ||||
|                                      const size_t bytesToRead); // Return bytes read
 | ||||
| typedef bool   PlatformAPI_FileOpen (const char *const path, PlatformFile *const file, const u32 permissionFlags, const enum PlatformFileAction actionFlags); | ||||
| typedef size_t PlatformAPI_FileRead (PlatformFile *const file, u8 *const buf, const size_t bytesToRead); // Return bytes read
 | ||||
| typedef size_t PlatformAPI_FileWrite(PlatformFile *const file, u8 *const buf, const size_t numBytesToWrite); // Return bytes read
 | ||||
| typedef void   PlatformAPI_FileClose(PlatformFile *const file); | ||||
| typedef void   PlatformAPI_Print    (const char *const string); | ||||
| typedef struct PlatformAPI | ||||
| { | ||||
| 	PlatformAPI_FileOpen  *FileOpen; | ||||
| 	PlatformAPI_FileRead  *FileRead; | ||||
| 	PlatformAPI_FileWrite *FileWrite; | ||||
| 	PlatformAPI_FileClose *FileClose; | ||||
| 	PlatformAPI_Print     *Print; | ||||
| } PlatformAPI; | ||||
| @ -68,6 +75,14 @@ typedef struct KeyState | ||||
| 	u32 halfTransitionCount; | ||||
| } KeyState; | ||||
| 
 | ||||
| typedef struct PlatformMouse | ||||
| { | ||||
| 	i32 x; | ||||
| 	i32 y; | ||||
| 	KeyState leftBtn; | ||||
| 	KeyState rightBtn; | ||||
| } PlatformMouse; | ||||
| 
 | ||||
| typedef struct PlatformInput | ||||
| { | ||||
| 	f32  deltaForFrame; | ||||
| @ -76,7 +91,8 @@ typedef struct PlatformInput | ||||
| 	bool canUseSSE2; | ||||
| 	bool canUseRdtsc; | ||||
| 
 | ||||
| 	PlatformAPI api; | ||||
| 	PlatformAPI   api; | ||||
| 	PlatformMouse mouse; | ||||
| 	union { | ||||
| 		KeyState key[key_count]; | ||||
| 		struct | ||||
|  | ||||
| @ -7,16 +7,11 @@ | ||||
| #include "external/stb_rect_pack.h" | ||||
| #include "external/stb_truetype.h" | ||||
| 
 | ||||
| // #define DTR_DEBUG_RENDER_FONT_BITMAP
 | ||||
| #ifdef DTR_DEBUG_RENDER_FONT_BITMAP | ||||
| 	#define STB_IMAGE_WRITE_IMPLEMENTATION | ||||
| 	#include "external/stb_image_write.h" | ||||
| #endif | ||||
| 
 | ||||
| FILE_SCOPE const f32 COLOR_EPSILON = 0.9f; | ||||
| 
 | ||||
| FILE_SCOPE inline DqnV4 PreMultiplyAlpha1(const DqnV4 color) | ||||
| { | ||||
| 	DQN_ASSERT(color.a >= 0.0f && color.a <= 1.0f); | ||||
| 	DqnV4 result; | ||||
| 	result.r = color.r * color.a; | ||||
| 	result.g = color.g * color.a; | ||||
| @ -26,7 +21,10 @@ FILE_SCOPE inline DqnV4 PreMultiplyAlpha1(const DqnV4 color) | ||||
| 	DQN_ASSERT(result.r >= 0.0f && result.r <= 1.0f); | ||||
| 	DQN_ASSERT(result.g >= 0.0f && result.g <= 1.0f); | ||||
| 	DQN_ASSERT(result.b >= 0.0f && result.b <= 1.0f); | ||||
| 	DQN_ASSERT(result.a >= 0.0f && result.a <= 1.0f); | ||||
| 
 | ||||
| 	DQN_ASSERT(result.a >= result.r); | ||||
| 	DQN_ASSERT(result.a >= result.g); | ||||
| 	DQN_ASSERT(result.a >= result.b); | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| @ -113,10 +111,8 @@ FILE_SCOPE inline void SetPixel(DTRRenderBuffer *const renderBuffer, const i32 x | ||||
| 	// If some alpha is involved, we need to apply gamma correction, but if the
 | ||||
| 	// new pixel is totally opaque or invisible then we're just flat out
 | ||||
| 	// overwriting/keeping the state of the pixel so we can save cycles by skipping.
 | ||||
| #if 1 | ||||
| 	bool needGammaFix = (color.a > 0.0f || color.a < 1.0f + COLOR_EPSILON) && (colorSpace == ColorSpace_SRGB); | ||||
| 	if (needGammaFix) color = DTRRender_SRGB1ToLinearSpaceV4(color); | ||||
| #endif | ||||
| 
 | ||||
| 	u32 src  = bitmapPtr[x + (y * pitchInU32)]; | ||||
| 	f32 srcR = (f32)((src >> 16) & 0xFF) * DTRRENDER_INV_255; | ||||
| @ -484,6 +480,237 @@ void DTRRender_Rectangle(DTRRenderBuffer *const renderBuffer, DqnV2 min, DqnV2 m | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| FILE_SCOPE void DebugBarycentricInternal(DqnV2 p, DqnV2 a, DqnV2 b, DqnV2 c, f32 *u, f32 *v, f32 *w) | ||||
| { | ||||
| 	DqnV2 v0 = b - a; | ||||
| 	DqnV2 v1 = c - a; | ||||
| 	DqnV2 v2 = p - a; | ||||
| 
 | ||||
| 	f32 d00   = DqnV2_Dot(v0, v0); | ||||
| 	f32 d01   = DqnV2_Dot(v0, v1); | ||||
| 	f32 d11   = DqnV2_Dot(v1, v1); | ||||
| 	f32 d20   = DqnV2_Dot(v2, v0); | ||||
| 	f32 d21   = DqnV2_Dot(v2, v1); | ||||
| 	f32 denom = d00 * d11 - d01 * d01; | ||||
| 	*v        = (d11 * d20 - d01 * d21) / denom; | ||||
| 	*w        = (d00 * d21 - d01 * d20) / denom; | ||||
| 	*u        = 1.0f - *v - *w; | ||||
| } | ||||
| 
 | ||||
| void DTRRender_TexturedTriangle(DTRRenderBuffer *const renderBuffer, DqnV3 p1, DqnV3 p2, DqnV3 p3, | ||||
|                                 DqnV2 uv1, DqnV2 uv2, DqnV2 uv3, DTRBitmap *const texture, | ||||
|                                 DqnV4 color, const DTRRenderTransform transform) | ||||
| { | ||||
| 	DTR_DEBUG_EP_TIMED_FUNCTION(); | ||||
| 	////////////////////////////////////////////////////////////////////////////
 | ||||
| 	// Transform vertexes
 | ||||
| 	////////////////////////////////////////////////////////////////////////////
 | ||||
| 	DqnV3 p1p2 = p2 - p1; | ||||
| 	DqnV3 p1p3 = p3 - p1; | ||||
| 
 | ||||
| 	// TODO(doyle): Transform is only in 2d right now
 | ||||
| 	DqnV2 p1p2Anchored = p1p2.xy * transform.anchor; | ||||
| 	DqnV2 p1p3Anchored = p1p3.xy * transform.anchor; | ||||
| 	DqnV2 origin       = p1.xy + p1p2Anchored + p1p3Anchored; | ||||
| 	DqnV2 pList[3]     = {p1.xy - origin, p2.xy - origin, p3.xy - origin}; | ||||
| 	TransformPoints(origin, pList, DQN_ARRAY_COUNT(pList), transform.scale, transform.rotation); | ||||
| 	p1.xy = pList[0]; | ||||
| 	p2.xy = pList[1]; | ||||
| 	p3.xy = pList[2]; | ||||
| 
 | ||||
| 	color = DTRRender_SRGB1ToLinearSpaceV4(color); | ||||
| 	color = PreMultiplyAlpha1(color); | ||||
| 
 | ||||
| 	////////////////////////////////////////////////////////////////////////////
 | ||||
| 	// Calculate Bounding Box
 | ||||
| 	////////////////////////////////////////////////////////////////////////////
 | ||||
| 	DqnV2i max = DqnV2i_2f(DQN_MAX(DQN_MAX(p1.x, p2.x), p3.x), | ||||
| 	                       DQN_MAX(DQN_MAX(p1.y, p2.y), p3.y)); | ||||
| 	DqnV2i min = DqnV2i_2f(DQN_MIN(DQN_MIN(p1.x, p2.x), p3.x), | ||||
| 	                       DQN_MIN(DQN_MIN(p1.y, p2.y), p3.y)); | ||||
| 	min.x = DQN_MAX(min.x, 0); | ||||
| 	min.y = DQN_MAX(min.y, 0); | ||||
| 	max.x = DQN_MIN(max.x, renderBuffer->width - 1); | ||||
| 	max.y = DQN_MIN(max.y, renderBuffer->height - 1); | ||||
| 
 | ||||
| 	f32 area2Times = ((p2.x - p1.x) * (p2.y + p1.y)) + | ||||
| 	                 ((p3.x - p2.x) * (p3.y + p2.y)) + | ||||
| 	                 ((p1.x - p3.x) * (p1.y + p3.y)); | ||||
| 	if (area2Times > 0) | ||||
| 	{ | ||||
| 		// Clockwise swap any point to make it clockwise
 | ||||
| 		DQN_SWAP(DqnV3, p2, p3); | ||||
| 	} | ||||
| 
 | ||||
| 	////////////////////////////////////////////////////////////////////////////
 | ||||
| 	// Signed Area - See Render_Triangle for explanation
 | ||||
| 	////////////////////////////////////////////////////////////////////////////
 | ||||
| 	const DqnV3 a = p1; | ||||
| 	const DqnV3 b = p2; | ||||
| 	const DqnV3 c = p3; | ||||
| 
 | ||||
| 	DqnV2i startP = min; | ||||
| 	f32 oldSignedArea1       = ((b.x - a.x) * (startP.y - a.y)) - ((b.y - a.y) * (startP.x - a.x)); | ||||
| 	f32 oldSignedArea2       = ((c.x - b.x) * (startP.y - b.y)) - ((c.y - b.y) * (startP.x - b.x)); | ||||
| 	f32 oldSignedArea3       = ((a.x - c.x) * (startP.y - c.y)) - ((a.y - c.y) * (startP.x - c.x)); | ||||
| 
 | ||||
| 	f32 signedArea1       = ((b.x - a.x) * (startP.y - a.y)) - ((b.y - a.y) * (startP.x - a.x)); | ||||
| 	f32 signedArea1DeltaX = a.y - b.y; | ||||
| 	f32 signedArea1DeltaY = b.x - a.x; | ||||
| 
 | ||||
| 	f32 signedArea2       = ((c.x - b.x) * (startP.y - b.y)) - ((c.y - b.y) * (startP.x - b.x)); | ||||
| 	f32 signedArea2DeltaX = b.y - c.y; | ||||
| 	f32 signedArea2DeltaY = c.x - b.x; | ||||
| 
 | ||||
| 	f32 signedArea3       = ((a.x - c.x) * (startP.y - c.y)) - ((a.y - c.y) * (startP.x - c.x)); | ||||
| 	f32 signedArea3DeltaX = c.y - a.y; | ||||
| 	f32 signedArea3DeltaY = a.x - c.x; | ||||
| 
 | ||||
| 	f32 signedAreaParallelogram = signedArea1 + signedArea2 + signedArea3; | ||||
| 	if (signedAreaParallelogram == 0) return; | ||||
| 	f32 invSignedAreaParallelogram = 1 / signedAreaParallelogram; | ||||
| 
 | ||||
| 	DTRDebug_BeginCycleCount(DTRDebugCycleCount_RenderTriangle_Rasterise); | ||||
| 	////////////////////////////////////////////////////////////////////////////
 | ||||
| 	// Scan and Render
 | ||||
| 	////////////////////////////////////////////////////////////////////////////
 | ||||
| 	const u32 zBufferPitch        = renderBuffer->width; | ||||
| 	const f32 BARYCENTRIC_EPSILON = 0.1f; | ||||
| 
 | ||||
| 	u8 *texturePtr         = texture->memory; | ||||
| 	const u32 texturePitch = texture->bytesPerPixel * texture->dim.w; | ||||
| 	for (i32 bufferY = min.y; bufferY < max.y; bufferY++) | ||||
| 	{ | ||||
| 		f32 signedArea1Row = signedArea1; | ||||
| 		f32 signedArea2Row = signedArea2; | ||||
| 		f32 signedArea3Row = signedArea3; | ||||
| 
 | ||||
| 		for (i32 bufferX = min.x; bufferX < max.x; bufferX++) | ||||
| 		{ | ||||
| 			if (signedArea1Row >= 0 && signedArea2Row >= 0 && signedArea3Row >= 0) | ||||
| 			{ | ||||
| 				f32 barycentricB = signedArea3Row * invSignedAreaParallelogram; | ||||
| 				f32 barycentricC = signedArea1Row * invSignedAreaParallelogram; | ||||
| 
 | ||||
| 				if (DTR_DEBUG) | ||||
| 				{ | ||||
| 					const f32 EPSILON = 0.1f; | ||||
| 
 | ||||
| 					f32 debugSignedArea1 = ((b.x - a.x) * (bufferY - a.y)) - ((b.y - a.y) * (bufferX - a.x)); | ||||
| 					f32 debugSignedArea2 = ((c.x - b.x) * (bufferY - b.y)) - ((c.y - b.y) * (bufferX - b.x)); | ||||
| 					f32 debugSignedArea3 = ((a.x - c.x) * (bufferY - c.y)) - ((a.y - c.y) * (bufferX - c.x)); | ||||
| 
 | ||||
| 					f32 deltaSignedArea1 = debugSignedArea1 - signedArea1Row; | ||||
| 					f32 deltaSignedArea2 = debugSignedArea2 - signedArea2Row; | ||||
| 					f32 deltaSignedArea3 = debugSignedArea3 - signedArea3Row; | ||||
| 					DQN_ASSERT(deltaSignedArea1 < EPSILON && deltaSignedArea2 < EPSILON && | ||||
| 					           deltaSignedArea3 < EPSILON) | ||||
| 
 | ||||
| 					f32 debugBarycentricA, debugBarycentricB, debugBarycentricC; | ||||
| 					DebugBarycentricInternal(DqnV2_2i(bufferX, bufferY), a.xy, b.xy, c.xy, | ||||
| 					                         &debugBarycentricA, &debugBarycentricB, | ||||
| 					                         &debugBarycentricC); | ||||
| 
 | ||||
| 
 | ||||
| 					f32 deltaBaryB = DQN_ABS(barycentricB - debugBarycentricB); | ||||
| 					f32 deltaBaryC = DQN_ABS(barycentricC - debugBarycentricC); | ||||
| 
 | ||||
| 					DQN_ASSERT(deltaBaryB < EPSILON && deltaBaryC < EPSILON) | ||||
| 				} | ||||
| 
 | ||||
| 				i32 zBufferIndex = bufferX + (bufferY * zBufferPitch); | ||||
| 				f32 pixelZValue = a.z + (barycentricB * (b.z - a.z)) + (barycentricC * (c.z - a.z)); | ||||
| 				f32 currZValue  = renderBuffer->zBuffer[zBufferIndex]; | ||||
| 				DQN_ASSERT(zBufferIndex < (renderBuffer->width * renderBuffer->height)); | ||||
| 
 | ||||
| 				if (pixelZValue > currZValue) | ||||
| 				{ | ||||
| 					renderBuffer->zBuffer[zBufferIndex] = pixelZValue; | ||||
| 					const bool DEBUG_SAMPLE_TEXTURE = true; | ||||
| 					DqnV2 uv = uv1 + ((uv2 - uv1) * barycentricB) + ((uv3 - uv1) * barycentricC); | ||||
| 
 | ||||
| 					const f32 EPSILON = 0.1f; | ||||
| 					DQN_ASSERT(uv.x >= 0 && uv.x < 1.0f + EPSILON); | ||||
| 					DQN_ASSERT(uv.y >= 0 && uv.y < 1.0f + EPSILON); | ||||
| 
 | ||||
| 					uv.x = DqnMath_Clampf(uv.x, 0.0f, 1.0f); | ||||
| 					uv.y = DqnMath_Clampf(uv.y, 0.0f, 1.0f); | ||||
| 
 | ||||
| 					f32 texelXf = uv.x * texture->dim.w; | ||||
| 					f32 texelYf = uv.y * texture->dim.h; | ||||
| 					DQN_ASSERT(texelXf >= 0 && texelXf < texture->dim.w); | ||||
| 					DQN_ASSERT(texelYf >= 0 && texelYf < texture->dim.h); | ||||
| 
 | ||||
| 					i32 texelX = (i32)texelXf; | ||||
| 					i32 texelY = (i32)texelYf; | ||||
| 
 | ||||
| 					u32 texel1 = *(u32 *)(texturePtr + (texelX * texture->bytesPerPixel) + | ||||
| 					                      (texelY * texturePitch)); | ||||
| 
 | ||||
| 					DqnV4 color1; | ||||
| 					color1.a = (f32)(texel1 >> 24); | ||||
| 					color1.b = (f32)((texel1 >> 16) & 0xFF); | ||||
| 					color1.g = (f32)((texel1 >> 8) & 0xFF); | ||||
| 					color1.r = (f32)((texel1 >> 0) & 0xFF); | ||||
| 
 | ||||
| 					color1 *= DTRRENDER_INV_255; | ||||
| 					color1      = DTRRender_SRGB1ToLinearSpaceV4(color1); | ||||
| 					DqnV4 blend = color * color1; | ||||
| 					SetPixel(renderBuffer, bufferX, bufferY, blend, ColorSpace_Linear); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			signedArea1Row += signedArea1DeltaX; | ||||
| 			signedArea2Row += signedArea2DeltaX; | ||||
| 			signedArea3Row += signedArea3DeltaX; | ||||
| 		} | ||||
| 
 | ||||
| 		signedArea1 += signedArea1DeltaY; | ||||
| 		signedArea2 += signedArea2DeltaY; | ||||
| 		signedArea3 += signedArea3DeltaY; | ||||
| 	} | ||||
| 	DTRDebug_EndCycleCount(DTRDebugCycleCount_RenderTriangle_Rasterise); | ||||
| 
 | ||||
| 	////////////////////////////////////////////////////////////////////////////
 | ||||
| 	// Debug
 | ||||
| 	////////////////////////////////////////////////////////////////////////////
 | ||||
| 	DTRDebug_CounterIncrement(DTRDebugCounter_RenderTriangle); | ||||
| 	if (DTR_DEBUG_RENDER) | ||||
| 	{ | ||||
| 		// Draw Bounding box
 | ||||
| 		if (0) | ||||
| 		{ | ||||
| 			DTRRender_Line(renderBuffer, DqnV2i_2i(min.x, min.y), DqnV2i_2i(min.x, max.y), color); | ||||
| 			DTRRender_Line(renderBuffer, DqnV2i_2i(min.x, max.y), DqnV2i_2i(max.x, max.y), color); | ||||
| 			DTRRender_Line(renderBuffer, DqnV2i_2i(max.x, max.y), DqnV2i_2i(max.x, min.y), color); | ||||
| 			DTRRender_Line(renderBuffer, DqnV2i_2i(max.x, min.y), DqnV2i_2i(min.x, min.y), color); | ||||
| 		} | ||||
| 
 | ||||
| 		// Draw Triangle Coordinate Basis
 | ||||
| 		if (0) | ||||
| 		{ | ||||
| 			DqnV2 xAxis = DqnV2_2f(cosf(transform.rotation), sinf(transform.rotation)) * transform.scale.x; | ||||
| 			DqnV2 yAxis         = DqnV2_2f(-xAxis.y, xAxis.x) * transform.scale.y; | ||||
| 			DqnV4 coordSysColor = DqnV4_4f(0, 1, 1, 1); | ||||
| 			i32 axisLen = 50; | ||||
| 			DTRRender_Line(renderBuffer, DqnV2i_V2(origin), DqnV2i_V2(origin) + DqnV2i_V2(xAxis * axisLen), coordSysColor); | ||||
| 			DTRRender_Line(renderBuffer, DqnV2i_V2(origin), DqnV2i_V2(origin) + DqnV2i_V2(yAxis * axisLen), coordSysColor); | ||||
| 		} | ||||
| 
 | ||||
| 		// Draw axis point
 | ||||
| 		if (0) | ||||
| 		{ | ||||
| 			DqnV4 green  = DqnV4_4f(0, 1, 0, 1); | ||||
| 			DqnV4 blue   = DqnV4_4f(0, 0, 1, 1); | ||||
| 			DqnV4 purple = DqnV4_4f(1, 0, 1, 1); | ||||
| 
 | ||||
| 			DTRRender_Rectangle(renderBuffer, p1.xy - DqnV2_1f(5), p1.xy + DqnV2_1f(5), green); | ||||
| 			DTRRender_Rectangle(renderBuffer, p2.xy - DqnV2_1f(5), p2.xy + DqnV2_1f(5), blue); | ||||
| 			DTRRender_Rectangle(renderBuffer, p3.xy - DqnV2_1f(5), p3.xy + DqnV2_1f(5), purple); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void DTRRender_Triangle(DTRRenderBuffer *const renderBuffer, DqnV3 p1, DqnV3 p2, DqnV3 p3, | ||||
|                         DqnV4 color, const DTRRenderTransform transform) | ||||
| { | ||||
| @ -670,30 +897,17 @@ void DTRRender_Triangle(DTRRenderBuffer *const renderBuffer, DqnV3 p1, DqnV3 p2, | ||||
| 		{ | ||||
| 			if (signedArea1Row >= 0 && signedArea2Row >= 0 && signedArea3Row >= 0) | ||||
| 			{ | ||||
| #if 1 | ||||
| 				f32 barycentricA = signedArea2Row * invSignedAreaParallelogram; | ||||
| 				f32 barycentricB = signedArea3Row * invSignedAreaParallelogram; | ||||
| 				f32 barycentricC = signedArea1Row * invSignedAreaParallelogram; | ||||
| 
 | ||||
| 				if (DTR_DEBUG) | ||||
| 				{ | ||||
| 					f32 barycentricSum = barycentricA + barycentricB + barycentricC; | ||||
| 					DQN_ASSERT((1.0f - barycentricSum) < BARYCENTRIC_EPSILON); | ||||
| 				} | ||||
| 
 | ||||
| 				f32 pixelZValue = | ||||
| 				    (a.z * barycentricA) + (b.z * barycentricB) + (c.z * barycentricC); | ||||
| 
 | ||||
| 				i32 zBufferIndex = bufferX + (bufferY * zBufferPitch); | ||||
| 				f32 currZValue   = renderBuffer->zBuffer[zBufferIndex]; | ||||
| 				f32 pixelZValue = a.z + (barycentricB * (b.z - a.z)) + (barycentricC * (c.z - a.z)); | ||||
| 				f32 currZValue  = renderBuffer->zBuffer[zBufferIndex]; | ||||
| 				if (pixelZValue > currZValue) | ||||
| 				{ | ||||
| 					renderBuffer->zBuffer[zBufferIndex] = pixelZValue; | ||||
| 					SetPixel(renderBuffer, bufferX, bufferY, color, ColorSpace_Linear); | ||||
| 				} | ||||
| #else | ||||
| 				SetPixel(renderBuffer, bufferX, bufferY, color, ColorSpace_Linear); | ||||
| #endif | ||||
| 			} | ||||
| 
 | ||||
| 			signedArea1Row += signedArea1DeltaX; | ||||
| @ -996,5 +1210,3 @@ void DTRRender_Clear(DTRRenderBuffer *const renderBuffer, | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -59,11 +59,12 @@ inline DqnV4 DTRRender_PreMultiplyAlphaSRGB1WithLinearConversion(DqnV4 color); | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // NOTE: All colors should be in the range of [0->1] where DqnV4 is a struct with 4 floats, rgba
 | ||||
| // Leaving len = -1 for text will make the system use strlen to determine len.
 | ||||
| void DTRRender_Text     (DTRRenderBuffer *const renderBuffer, const DTRFont font, DqnV2 pos, const char *const text, DqnV4 color = DqnV4_1f(1), i32 len = -1); | ||||
| void DTRRender_Line     (DTRRenderBuffer *const renderBuffer, DqnV2i a, DqnV2i b, DqnV4 color); | ||||
| void DTRRender_Rectangle(DTRRenderBuffer *const renderBuffer, DqnV2 min, DqnV2 max, DqnV4 color, const DTRRenderTransform transform = DTRRender_DefaultTransform()); | ||||
| void DTRRender_Triangle (DTRRenderBuffer *const renderBuffer, DqnV3 p1, DqnV3 p2, DqnV3 p3, DqnV4 color, const DTRRenderTransform transform = DTRRender_DefaultTriangleTransform()); | ||||
| void DTRRender_Bitmap   (DTRRenderBuffer *const renderBuffer, DTRBitmap *const bitmap, DqnV2 pos, const DTRRenderTransform transform = DTRRender_DefaultTransform(), DqnV4 color = DqnV4_4f(1, 1, 1, 1)); | ||||
| void DTRRender_Clear    (DTRRenderBuffer *const renderBuffer, DqnV3 color); | ||||
| void DTRRender_Text            (DTRRenderBuffer *const renderBuffer, const DTRFont font, DqnV2 pos, const char *const text, DqnV4 color = DqnV4_1f(1), i32 len = -1); | ||||
| void DTRRender_Line            (DTRRenderBuffer *const renderBuffer, DqnV2i a, DqnV2i b, DqnV4 color); | ||||
| void DTRRender_Rectangle       (DTRRenderBuffer *const renderBuffer, DqnV2 min, DqnV2 max, DqnV4 color, const DTRRenderTransform transform = DTRRender_DefaultTransform()); | ||||
| void DTRRender_Triangle        (DTRRenderBuffer *const renderBuffer, DqnV3 p1, DqnV3 p2, DqnV3 p3, DqnV4 color, const DTRRenderTransform transform = DTRRender_DefaultTriangleTransform()); | ||||
| void DTRRender_TexturedTriangle(DTRRenderBuffer *const renderBuffer, DqnV3 p1, DqnV3 p2, DqnV3 p3, DqnV2 uv1, DqnV2 uv2, DqnV2 uv3, DTRBitmap *const texture, DqnV4 color, const DTRRenderTransform transform = DTRRender_DefaultTriangleTransform()); | ||||
| void DTRRender_Bitmap          (DTRRenderBuffer *const renderBuffer, DTRBitmap *const bitmap, DqnV2 pos, const DTRRenderTransform transform = DTRRender_DefaultTransform(), DqnV4 color = DqnV4_4f(1, 1, 1, 1)); | ||||
| void DTRRender_Clear           (DTRRenderBuffer *const renderBuffer, DqnV3 color); | ||||
| 
 | ||||
| #endif | ||||
|  | ||||
| @ -40,16 +40,20 @@ void Platform_Print(const char *const string) | ||||
| 	OutputDebugString(string); | ||||
| } | ||||
| 
 | ||||
| bool Platform_FileOpen(const char *const path, PlatformFile *const file, | ||||
|                        const u32 permissionFlags) | ||||
| bool Platform_FileOpen(const char *const path, PlatformFile *const file, const u32 permissionFlags, | ||||
|                        const enum PlatformFileAction fileAction) | ||||
| { | ||||
| 	if (!path || !file) return false; | ||||
| 	DQN_ASSERT((permissionFlags & | ||||
| 	            ~(PlatformFilePermissionFlag_Write | | ||||
| 	              PlatformFilePermissionFlag_Read)) == 0); | ||||
| 
 | ||||
| 	DQN_ASSERT((fileAction & | ||||
| 	            ~(PlatformFileAction_OpenOnly | PlatformFileAction_CreateIfNotExist | | ||||
| 	              PlatformFileAction_ClearIfExist)) == 0); | ||||
| 
 | ||||
| 	DqnFile dqnFile = {}; | ||||
| 	if (DqnFile_Open(path, &dqnFile, permissionFlags, DqnFileAction_OpenOnly)) | ||||
| 	if (DqnFile_Open(path, &dqnFile, permissionFlags, (enum DqnFileAction)fileAction)) | ||||
| 	{ | ||||
| 		*file = DqnFileToPlatformFileInternal(dqnFile); | ||||
| 		return true; | ||||
| @ -69,6 +73,17 @@ size_t Platform_FileRead(PlatformFile *const file, u8 *const buf, | ||||
| 	return numBytesRead; | ||||
| } | ||||
| 
 | ||||
| size_t Platform_FileWrite(PlatformFile *const file, u8 *const buf, | ||||
|                          const size_t numBytesToWrite) | ||||
| { | ||||
| 	if (!file || !buf) return 0; | ||||
| 
 | ||||
| 	DqnFile dqnFile     = PlatformFileToDqnFileInternal(*file); | ||||
| 	size_t numBytesRead = DqnFile_Write(&dqnFile, buf, numBytesToWrite, 0); | ||||
| 	return numBytesRead; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void Platform_FileClose(PlatformFile *const file) | ||||
| { | ||||
| 	if (!file) return; | ||||
| @ -81,6 +96,7 @@ void Platform_FileClose(PlatformFile *const file) | ||||
| // Win32 Layer
 | ||||
| ////////////////////////////////////////////////////////////////////////////////
 | ||||
| #include <Windows.h> | ||||
| #include <Windowsx.h> // For GET_X|Y_LPARAM(), mouse input
 | ||||
| #include <Psapi.h>    // For win32 GetProcessMemoryInfo()
 | ||||
| typedef struct Win32RenderBitmap | ||||
| { | ||||
| @ -314,6 +330,34 @@ FILE_SCOPE void Win32ProcessMessages(HWND window, PlatformInput *input) | ||||
| 			} | ||||
| 			break; | ||||
| 
 | ||||
| 			case WM_LBUTTONDOWN: | ||||
| 			case WM_RBUTTONDOWN: | ||||
| 			case WM_LBUTTONUP: | ||||
| 			case WM_RBUTTONUP: | ||||
| 			{ | ||||
| 				bool isDown = (msg.message == WM_LBUTTONDOWN || msg.message == WM_RBUTTONDOWN); | ||||
| 
 | ||||
| 				if (msg.message == WM_LBUTTONDOWN || msg.message == WM_LBUTTONUP) | ||||
| 				{ | ||||
| 					Win32UpdateKey(&input->mouse.leftBtn, isDown); | ||||
| 				} | ||||
| 				else if (msg.message == WM_RBUTTONDOWN || msg.message == WM_RBUTTONUP) | ||||
| 				{ | ||||
| 					Win32UpdateKey(&input->mouse.rightBtn, isDown); | ||||
| 				} | ||||
| 			} | ||||
| 			break; | ||||
| 
 | ||||
| 			case WM_MOUSEMOVE: | ||||
| 			{ | ||||
| 				LONG height; | ||||
| 				DqnWin32_GetClientDim(window, NULL, &height); | ||||
| 
 | ||||
| 				input->mouse.x = GET_X_LPARAM(msg.lParam); | ||||
| 				input->mouse.y = height - GET_Y_LPARAM(msg.lParam); | ||||
| 			} | ||||
| 			break; | ||||
| 
 | ||||
| 			case WM_SYSKEYDOWN: | ||||
| 			case WM_SYSKEYUP: | ||||
| 			case WM_KEYDOWN: | ||||
| @ -431,8 +475,8 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, | ||||
| 	// when you blit to the screen blackness, the area that is being blitted to
 | ||||
| 	// is slightly smaller than 800x600. Windows provides a function to help
 | ||||
| 	// calculate the size you'd need by accounting for the window style.
 | ||||
| 	const u32 MIN_WIDTH  = 1024; | ||||
| 	const u32 MIN_HEIGHT = 768; | ||||
| 	const u32 MIN_WIDTH  = 800; | ||||
| 	const u32 MIN_HEIGHT = 800; | ||||
| 	RECT rect   = {}; | ||||
| 	rect.right  = MIN_WIDTH; | ||||
| 	rect.bottom = MIN_HEIGHT; | ||||
| @ -508,6 +552,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, | ||||
| 	PlatformAPI platformAPI = {}; | ||||
| 	platformAPI.FileOpen    = Platform_FileOpen; | ||||
| 	platformAPI.FileRead    = Platform_FileRead; | ||||
| 	platformAPI.FileWrite   = Platform_FileWrite; | ||||
| 	platformAPI.FileClose   = Platform_FileClose; | ||||
| 	platformAPI.Print       = Platform_Print; | ||||
| 
 | ||||
| @ -588,8 +633,9 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		frameTimeInS   = DqnTime_NowInS() - startFrameTimeInS; | ||||
| 		f32 msPerFrame = 1000.0f * (f32)frameTimeInS; | ||||
| 		frameTimeInS        = DqnTime_NowInS() - startFrameTimeInS; | ||||
| 		f32 msPerFrame      = 1000.0f * (f32)frameTimeInS; | ||||
| 		f32 framesPerSecond = 1.0f / (f32)frameTimeInS; | ||||
| 
 | ||||
| 		////////////////////////////////////////////////////////////////////////
 | ||||
| 		// Misc
 | ||||
| @ -600,7 +646,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, | ||||
| 
 | ||||
| 		// Update title bar
 | ||||
| 		char windowTitleBuffer[128] = {}; | ||||
| 		Dqn_sprintf(windowTitleBuffer, "drenderer - dev - %5.2f ms/f - mem %'dkb", msPerFrame, | ||||
| 		Dqn_sprintf(windowTitleBuffer, "drenderer - dev - %5.2f ms/f - %5.2f fps - mem %'dkb", msPerFrame, framesPerSecond, | ||||
| 		            (u32)(memCounter.PagefileUsage / 1024.0f)); | ||||
| 		SetWindowTextA(mainWindow, windowTitleBuffer); | ||||
| 	} | ||||
|  | ||||
| @ -36,7 +36,7 @@ typedef int16_t i16; | ||||
| typedef double f64; | ||||
| typedef float  f32; | ||||
| 
 | ||||
| #define DQN_F32_MIN FLT_MIN | ||||
| #define DQN_F32_MIN -FLT_MAX | ||||
| 
 | ||||
| #define DQN_TERABYTE(val) (DQN_GIGABYTE(val) * 1024LL) | ||||
| #define DQN_GIGABYTE(val) (DQN_MEGABYTE(val) * 1024LL) | ||||
| @ -766,7 +766,6 @@ DQN_FILE_SCOPE i32  DqnRnd_PCGRange(DqnRandPCGState *pcg, i32 min, i32 max); | ||||
| #define WIN32_LEAN_AND_MEAN | ||||
| #include <Windows.h> | ||||
| 
 | ||||
| 
 | ||||
| #define DQN_WIN32_ERROR_BOX(text, title) MessageBoxA(NULL, text, title, MB_OK); | ||||
| // Out is a pointer to the buffer to receive the characters.
 | ||||
| // outLen is the length/capacity of the out buffer
 | ||||
|  | ||||
							
								
								
									
										1092
									
								
								src/external/tests/stb_image_write.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1092
									
								
								src/external/tests/stb_image_write.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										221
									
								
								src/external/tests/tinyrenderer/geometry.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								src/external/tests/tinyrenderer/geometry.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,221 @@ | ||||
| #ifndef __GEOMETRY_H__ | ||||
| #define __GEOMETRY_H__ | ||||
| #include <cmath> | ||||
| #include <vector> | ||||
| #include <cassert> | ||||
| #include <iostream> | ||||
| 
 | ||||
| template<size_t DimCols,size_t DimRows,typename T> class mat; | ||||
| 
 | ||||
| template <size_t DIM, typename T> struct vec { | ||||
|     vec() { for (size_t i=DIM; i--; data_[i] = T()); } | ||||
|           T& operator[](const size_t i)       { assert(i<DIM); return data_[i]; } | ||||
|     const T& operator[](const size_t i) const { assert(i<DIM); return data_[i]; } | ||||
| private: | ||||
|     T data_[DIM]; | ||||
| }; | ||||
| 
 | ||||
| /////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| template <typename T> struct vec<2,T> { | ||||
|     vec() : x(T()), y(T()) {} | ||||
|     vec(T X, T Y) : x(X), y(Y) {} | ||||
|     template <class U> vec<2,T>(const vec<2,U> &v); | ||||
|           T& operator[](const size_t i)       { assert(i<2); return i<=0 ? x : y; } | ||||
|     const T& operator[](const size_t i) const { assert(i<2); return i<=0 ? x : y; } | ||||
| 
 | ||||
|     T x,y; | ||||
| }; | ||||
| 
 | ||||
| /////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| template <typename T> struct vec<3,T> { | ||||
|     vec() : x(T()), y(T()), z(T()) {} | ||||
|     vec(T X, T Y, T Z) : x(X), y(Y), z(Z) {} | ||||
|     template <class U> vec<3,T>(const vec<3,U> &v); | ||||
|           T& operator[](const size_t i)       { assert(i<3); return i<=0 ? x : (1==i ? y : z); } | ||||
|     const T& operator[](const size_t i) const { assert(i<3); return i<=0 ? x : (1==i ? y : z); } | ||||
|     float norm() { return std::sqrt(x*x+y*y+z*z); } | ||||
|     vec<3,T> & normalize(T l=1) { *this = (*this)*(l/norm()); return *this; } | ||||
| 
 | ||||
|     T x,y,z; | ||||
| }; | ||||
| 
 | ||||
| /////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| template<size_t DIM,typename T> T operator*(const vec<DIM,T>& lhs, const vec<DIM,T>& rhs) { | ||||
|     T ret = T(); | ||||
|     for (size_t i=DIM; i--; ret+=lhs[i]*rhs[i]); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template<size_t DIM,typename T>vec<DIM,T> operator+(vec<DIM,T> lhs, const vec<DIM,T>& rhs) { | ||||
|     for (size_t i=DIM; i--; lhs[i]+=rhs[i]); | ||||
|     return lhs; | ||||
| } | ||||
| 
 | ||||
| template<size_t DIM,typename T>vec<DIM,T> operator-(vec<DIM,T> lhs, const vec<DIM,T>& rhs) { | ||||
|     for (size_t i=DIM; i--; lhs[i]-=rhs[i]); | ||||
|     return lhs; | ||||
| } | ||||
| 
 | ||||
| template<size_t DIM,typename T,typename U> vec<DIM,T> operator*(vec<DIM,T> lhs, const U& rhs) { | ||||
|     for (size_t i=DIM; i--; lhs[i]*=rhs); | ||||
|     return lhs; | ||||
| } | ||||
| 
 | ||||
| template<size_t DIM,typename T,typename U> vec<DIM,T> operator/(vec<DIM,T> lhs, const U& rhs) { | ||||
|     for (size_t i=DIM; i--; lhs[i]/=rhs); | ||||
|     return lhs; | ||||
| } | ||||
| 
 | ||||
| template<size_t LEN,size_t DIM,typename T> vec<LEN,T> embed(const vec<DIM,T> &v, T fill=1) { | ||||
|     vec<LEN,T> ret; | ||||
|     for (size_t i=LEN; i--; ret[i]=(i<DIM?v[i]:fill)); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| template<size_t LEN,size_t DIM, typename T> vec<LEN,T> proj(const vec<DIM,T> &v) { | ||||
|     vec<LEN,T> ret; | ||||
|     for (size_t i=LEN; i--; ret[i]=v[i]); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| template <typename T> vec<3,T> cross(vec<3,T> v1, vec<3,T> v2) { | ||||
|     return vec<3,T>(v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x); | ||||
| } | ||||
| 
 | ||||
| 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: | ||||
|     mat() {} | ||||
| 
 | ||||
|     vec<DimCols,T>& operator[] (const size_t idx) { | ||||
|         assert(idx<DimRows); | ||||
|         return rows[idx]; | ||||
|     } | ||||
| 
 | ||||
|     const vec<DimCols,T>& operator[] (const size_t idx) const { | ||||
|         assert(idx<DimRows); | ||||
|         return rows[idx]; | ||||
|     } | ||||
| 
 | ||||
|     vec<DimRows,T> col(const size_t idx) const { | ||||
|         assert(idx<DimCols); | ||||
|         vec<DimRows,T> ret; | ||||
|         for (size_t i=DimRows; i--; ret[i]=rows[i][idx]); | ||||
|         return ret; | ||||
|     } | ||||
| 
 | ||||
|     void set_col(size_t idx, vec<DimRows,T> v) { | ||||
|         assert(idx<DimCols); | ||||
|         for (size_t i=DimRows; i--; rows[i][idx]=v[i]); | ||||
|     } | ||||
| 
 | ||||
|     static mat<DimRows,DimCols,T> identity() { | ||||
|         mat<DimRows,DimCols,T> ret; | ||||
|         for (size_t i=DimRows; i--; ) | ||||
|             for (size_t j=DimCols;j--; ret[i][j]=(i==j)); | ||||
|         return ret; | ||||
|     } | ||||
| 
 | ||||
|     T det() const { | ||||
|         return dt<DimCols,T>::det(*this); | ||||
|     } | ||||
| 
 | ||||
|     mat<DimRows-1,DimCols-1,T> get_minor(size_t row, size_t col) const { | ||||
|         mat<DimRows-1,DimCols-1,T> ret; | ||||
|         for (size_t i=DimRows-1; i--; ) | ||||
|             for (size_t j=DimCols-1;j--; ret[i][j]=rows[i<row?i:i+1][j<col?j:j+1]); | ||||
|         return ret; | ||||
|     } | ||||
| 
 | ||||
|     T cofactor(size_t row, size_t col) const { | ||||
|         return get_minor(row,col).det()*((row+col)%2 ? -1 : 1); | ||||
|     } | ||||
| 
 | ||||
|     mat<DimRows,DimCols,T> adjugate() const { | ||||
|         mat<DimRows,DimCols,T> ret; | ||||
|         for (size_t i=DimRows; i--; ) | ||||
|             for (size_t j=DimCols; j--; ret[i][j]=cofactor(i,j)); | ||||
|         return ret; | ||||
|     } | ||||
| 
 | ||||
|     mat<DimRows,DimCols,T> invert_transpose() { | ||||
|         mat<DimRows,DimCols,T> ret = adjugate(); | ||||
|         T tmp = ret[0]*rows[0]; | ||||
|         return ret/tmp; | ||||
|     } | ||||
| 
 | ||||
|     mat<DimRows,DimCols,T> invert() { | ||||
|         return invert_transpose().transpose(); | ||||
|     } | ||||
| 
 | ||||
|     mat<DimCols,DimRows,T> transpose() { | ||||
|         mat<DimCols,DimRows,T> ret; | ||||
|         for (size_t i=DimCols; i--; ret[i]=this->col(i)); | ||||
|         return ret; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| template<size_t DimRows,size_t DimCols,typename T> vec<DimRows,T> operator*(const mat<DimRows,DimCols,T>& lhs, const vec<DimCols,T>& rhs) { | ||||
|     vec<DimRows,T> ret; | ||||
|     for (size_t i=DimRows; i--; ret[i]=lhs[i]*rhs); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| template<size_t R1,size_t C1,size_t C2,typename T>mat<R1,C2,T> operator*(const mat<R1,C1,T>& lhs, const mat<C1,C2,T>& rhs) { | ||||
|     mat<R1,C2,T> result; | ||||
|     for (size_t i=R1; i--; ) | ||||
|         for (size_t j=C2; j--; result[i][j]=lhs[i]*rhs.col(j)); | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| template<size_t DimRows,size_t DimCols,typename T>mat<DimCols,DimRows,T> operator/(mat<DimRows,DimCols,T> lhs, const T& rhs) { | ||||
|     for (size_t i=DimRows; i--; lhs[i]=lhs[i]/rhs); | ||||
|     return lhs; | ||||
| } | ||||
| 
 | ||||
| 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__
 | ||||
| 
 | ||||
							
								
								
									
										54
									
								
								src/external/tests/tinyrenderer/model.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/external/tests/tinyrenderer/model.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | ||||
| #include <iostream> | ||||
| #include <string> | ||||
| #include <fstream> | ||||
| #include <sstream> | ||||
| #include <vector> | ||||
| #include "model.h" | ||||
| 
 | ||||
| Model::Model(const char *filename) : verts_(), faces_() { | ||||
|     std::ifstream in; | ||||
|     in.open (filename, std::ifstream::in); | ||||
|     if (in.fail()) return; | ||||
|     std::string line; | ||||
|     while (!in.eof()) { | ||||
|         std::getline(in, line); | ||||
|         std::istringstream iss(line.c_str()); | ||||
|         char trash; | ||||
|         if (!line.compare(0, 2, "v ")) { | ||||
|             iss >> trash; | ||||
|             Vec3f v; | ||||
|             for (int i=0;i<3;i++) iss >> v[i]; | ||||
|             verts_.push_back(v); | ||||
|         } else if (!line.compare(0, 2, "f ")) { | ||||
|             std::vector<int> f; | ||||
|             int itrash, idx; | ||||
|             iss >> trash; | ||||
|             while (iss >> idx >> trash >> itrash >> trash >> itrash) { | ||||
|                 idx--; // in wavefront obj all indices start at 1, not zero
 | ||||
|                 f.push_back(idx); | ||||
|             } | ||||
|             faces_.push_back(f); | ||||
|         } | ||||
|     } | ||||
|     std::cerr << "# v# " << verts_.size() << " f# "  << faces_.size() << std::endl; | ||||
| } | ||||
| 
 | ||||
| Model::~Model() { | ||||
| } | ||||
| 
 | ||||
| int Model::nverts() { | ||||
|     return (int)verts_.size(); | ||||
| } | ||||
| 
 | ||||
| int Model::nfaces() { | ||||
|     return (int)faces_.size(); | ||||
| } | ||||
| 
 | ||||
| std::vector<int> Model::face(int idx) { | ||||
|     return faces_[idx]; | ||||
| } | ||||
| 
 | ||||
| Vec3f Model::vert(int i) { | ||||
|     return verts_[i]; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										20
									
								
								src/external/tests/tinyrenderer/model.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/external/tests/tinyrenderer/model.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| #ifndef __MODEL_H__ | ||||
| #define __MODEL_H__ | ||||
| 
 | ||||
| #include <vector> | ||||
| #include "geometry.h" | ||||
| 
 | ||||
| class Model { | ||||
| private: | ||||
| 	std::vector<Vec3f> verts_; | ||||
| 	std::vector<std::vector<int> > faces_; | ||||
| public: | ||||
| 	Model(const char *filename); | ||||
| 	~Model(); | ||||
| 	int nverts(); | ||||
| 	int nfaces(); | ||||
| 	Vec3f vert(int i); | ||||
| 	std::vector<int> face(int idx); | ||||
| }; | ||||
| 
 | ||||
| #endif //__MODEL_H__
 | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user