main.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. /*
  2. * 3D Engine
  3. * main.c:
  4. * Based on pikuma.com 3D software renderer in C
  5. * Copyright (c) 2021 986-Studio. All rights reserved.
  6. *
  7. * Created by Manoël Trapier on 01/03/2021.
  8. */
  9. #include <stdio.h>
  10. #include <stdint.h>
  11. #include <stdbool.h>
  12. #include <libc.h>
  13. #include <SDL.h>
  14. #include <log.h>
  15. #include <array.h>
  16. #include <matrix.h>
  17. #include <triangle.h>
  18. #include <display.h>
  19. #include <vector.h>
  20. #include <mesh.h>
  21. #include <light.h>
  22. #include <camera.h>
  23. bool isRunning = true;
  24. int previousFrameTime = 0;
  25. double deltaTime = 0;
  26. typedef enum displayMode_t
  27. {
  28. DISPLAY_MODE_WIREFRAME,
  29. DISPLAY_MODE_SOLID,
  30. DISPLAY_MODE_SOLID_WIRE,
  31. DISPLAY_MODE_TEXTURED,
  32. DISPLAY_MODE_TEXTURED_WIRE,
  33. } displayMode_t;
  34. bool displayVertex = false;
  35. bool doNotCull = false;
  36. bool doZBuffer = true;
  37. enum displayMode_t currentDisplayMode = DISPLAY_MODE_SOLID;
  38. matrix4_t projectionMatrix;
  39. matrix4_t viewMatrix;
  40. light_t globalLight;
  41. double updateTime, renderTime, waitTime;
  42. int trianglesCount, trianglesTotal;
  43. triangle_t *projectedTriangles = NULL;
  44. const vec3_t origin = {0, 0, 0 };
  45. const vec3_t up = {0, 1, 0 };
  46. void setup()
  47. {
  48. frameBuffer = (uint32_t *)calloc(windowWidth * windowHeight, sizeof(uint32_t));
  49. if (!frameBuffer)
  50. {
  51. Log(TLOG_PANIC, NULL, "Memory allocation error.");
  52. goto exit;
  53. }
  54. zBuffer = (double *)calloc(windowWidth * windowHeight, sizeof(double));
  55. if (!frameBuffer)
  56. {
  57. Log(TLOG_PANIC, NULL, "Memory allocation error.");
  58. goto exit;
  59. }
  60. frameBufferTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, windowWidth,
  61. windowHeight);
  62. if (frameBufferTexture == NULL)
  63. {
  64. Log(TLOG_PANIC, NULL, "SDL Texture creation error: %s", SDL_GetError());
  65. goto exit;
  66. }
  67. projectionMatrix = mat4Perspective((double)windowHeight / windowWidth, 90, 0.1, 100);
  68. globalLight.direction.x = 0;
  69. globalLight.direction.y = 0;
  70. globalLight.direction.z = 1;
  71. vec3Normalize(&globalLight.direction);
  72. /* Load texture */
  73. //meshTexture = (colour_t *)REDBRICK_TEXTURE;
  74. loadTextureDateFromPng("assets/f22.png");
  75. /* Load object */
  76. loadOBJFile("assets/f22.obj");
  77. //loadCubeMeshData();
  78. trianglesTotal = arrayGetSize(mesh.faces);
  79. exit:
  80. return;
  81. }
  82. void processInput()
  83. {
  84. SDL_Event event;
  85. SDL_PollEvent(&event);
  86. switch (event.type)
  87. {
  88. case SDL_QUIT:isRunning = false;
  89. break;
  90. case SDL_KEYDOWN:
  91. switch(event.key.keysym.sym)
  92. {
  93. case SDLK_ESCAPE:
  94. isRunning = false;
  95. break;
  96. case SDLK_1:
  97. displayVertex = true;
  98. currentDisplayMode = DISPLAY_MODE_WIREFRAME;
  99. break;
  100. case SDLK_2:
  101. displayVertex = false;
  102. currentDisplayMode = DISPLAY_MODE_WIREFRAME;
  103. break;
  104. case SDLK_3:
  105. displayVertex = false;
  106. currentDisplayMode = DISPLAY_MODE_SOLID;
  107. break;
  108. case SDLK_4:
  109. displayVertex = false;
  110. currentDisplayMode = DISPLAY_MODE_SOLID_WIRE;
  111. break;
  112. case SDLK_5:
  113. displayVertex = false;
  114. currentDisplayMode = DISPLAY_MODE_TEXTURED;
  115. break;
  116. case SDLK_6:
  117. displayVertex = false;
  118. currentDisplayMode = DISPLAY_MODE_TEXTURED_WIRE;
  119. break;
  120. case SDLK_c:
  121. doNotCull = false;
  122. break;
  123. case SDLK_d:
  124. doNotCull = true;
  125. break;
  126. case SDLK_f:
  127. doPerpectiveCorrection = false;
  128. break;
  129. case SDLK_v:
  130. doPerpectiveCorrection = true;
  131. break;
  132. case SDLK_b:
  133. doZBuffer = true;
  134. break;
  135. case SDLK_g:
  136. doZBuffer = false;
  137. break;
  138. }
  139. break;
  140. default:
  141. break;
  142. }
  143. }
  144. uint32_t getMicroSecTime()
  145. {
  146. struct timeval curTime;
  147. gettimeofday(&curTime, NULL);
  148. return ((curTime.tv_sec) * 1000 * 1000) + (curTime.tv_usec);
  149. }
  150. void update()
  151. {
  152. uint32_t i, j;
  153. uint32_t faceCount;
  154. uint32_t timeToWait = FRAME_TARGET_TIME - (SDL_GetTicks() - previousFrameTime);
  155. matrix4_t worldMatrix;
  156. uint32_t ticks = getMicroSecTime();
  157. vec3_t target = { 0, 0, 4 };
  158. if (timeToWait <= FRAME_TARGET_TIME)
  159. {
  160. SDL_Delay(timeToWait);
  161. }
  162. deltaTime = (SDL_GetTicks() - previousFrameTime) / 1000.0;
  163. previousFrameTime = SDL_GetTicks();
  164. waitTime = (waitTime + (getMicroSecTime() - ticks) / 1000.) / 2.0;
  165. ticks = getMicroSecTime();
  166. mesh.rotation.x += 0.6 * deltaTime;
  167. mesh.rotation.y += 0.6 * deltaTime;
  168. mesh.rotation.z += 0.6 * deltaTime;
  169. mesh.translation.z = 5;
  170. camera.position.x += 0.0 * deltaTime;
  171. camera.position.y += 0.0 * deltaTime;
  172. viewMatrix = mat4LookAt(camera.position, target, up);
  173. worldMatrix = mat4Identity;
  174. worldMatrix = mat4ProdMat4(mat4Scale(mesh.scale.x, mesh.scale.y, mesh.scale.z), worldMatrix);
  175. worldMatrix = mat4ProdMat4(mat4RotationZ(mesh.rotation.z), worldMatrix);
  176. worldMatrix = mat4ProdMat4(mat4RotationY(mesh.rotation.y), worldMatrix);
  177. worldMatrix = mat4ProdMat4(mat4RotationX(mesh.rotation.x), worldMatrix);
  178. worldMatrix = mat4ProdMat4(mat4Translate(mesh.translation.x, mesh.translation.y, mesh.translation.z), worldMatrix);
  179. /* Not sure for now */
  180. //worldMatrix = mat4ProdMat4(viewMatrix, worldMatrix);
  181. arrayEmpty(projectedTriangles);
  182. faceCount = arrayGetSize(mesh.faces);
  183. for(i = 0; i < faceCount; i++)
  184. {
  185. triangle_t currentTriangle =
  186. {
  187. .textureCoordinates =
  188. {
  189. [0] = { mesh.faces[i].a_uv.u, mesh.faces[i].a_uv.v },
  190. [1] = { mesh.faces[i].b_uv.u, mesh.faces[i].b_uv.v },
  191. [2] = { mesh.faces[i].c_uv.u, mesh.faces[i].c_uv.v }
  192. }
  193. };
  194. vec4_t transformedVertices[3];
  195. vec3_t triangleNormal;
  196. vec3_t vertices[3] =
  197. {
  198. mesh.vertices[mesh.faces[i].a],
  199. mesh.vertices[mesh.faces[i].b],
  200. mesh.vertices[mesh.faces[i].c],
  201. };
  202. for(j = 0; j < 3; j++)
  203. {
  204. transformedVertices[j] = vec4FromVec3(vertices[j]);
  205. transformedVertices[j] = mat4ProdVec4(worldMatrix, transformedVertices[j]);
  206. transformedVertices[j] = mat4ProdVec4(viewMatrix, transformedVertices[j]);
  207. }
  208. vec3_t vectorBA = vec3SubVectors(vec3FromVec4(transformedVertices[1]), vec3FromVec4(transformedVertices[0]));
  209. vec3_t vectorCA = vec3SubVectors(vec3FromVec4(transformedVertices[2]), vec3FromVec4(transformedVertices[0]));
  210. vec3Normalize(&vectorBA);
  211. vec3Normalize(&vectorCA);
  212. triangleNormal = vec3Cross(vectorBA, vectorCA);
  213. vec3Normalize(&triangleNormal);
  214. if (!doNotCull)
  215. {
  216. vec3_t cameraRay = vec3SubVectors(origin, vec3FromVec4(transformedVertices[0]));
  217. vec3Normalize(&cameraRay);
  218. double cameraDotTriangle = vec3Dot(triangleNormal, cameraRay);
  219. if (cameraDotTriangle < 0)
  220. {
  221. continue;
  222. }
  223. }
  224. for (j = 0 ; j < 3 ; j++)
  225. {
  226. vec4_t projected = mat4ProdVec4Project(projectionMatrix, transformedVertices[j]);
  227. projected.x *= windowWidth / 2.;
  228. projected.y *= windowHeight / 2.;
  229. /* Screen Y axis increase down, 3D world Y grows up, so we need to invert. */
  230. projected.y *= -1.0;
  231. projected.x += (windowWidth / 2.);
  232. projected.y += (windowHeight / 2.);
  233. currentTriangle.points[j].x = projected.x;
  234. currentTriangle.points[j].y = projected.y;
  235. currentTriangle.points[j].w = projected.w;
  236. currentTriangle.points[j].z = projected.z;
  237. }
  238. double lightLevel = -vec3Dot(triangleNormal, globalLight.direction);
  239. currentTriangle.colour = applyLight(mesh.faces[i].colour, lightLevel);
  240. currentTriangle.averageDepth = (transformedVertices[0].z +
  241. transformedVertices[1].z +
  242. transformedVertices[2].z) / 3.0;
  243. currentTriangle.texture = meshTexture;
  244. arrayAdd(projectedTriangles, currentTriangle);
  245. }
  246. if (!doZBuffer)
  247. {
  248. trianglesCount = arrayGetSize(projectedTriangles);
  249. qsort(projectedTriangles, trianglesCount, sizeof(triangle_t), compareTrianglesZOrder);
  250. }
  251. updateTime = (updateTime + (getMicroSecTime() - ticks) / 1000.) / 2.0;
  252. }
  253. void render()
  254. {
  255. int i;
  256. uint32_t ticks = getMicroSecTime();
  257. SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
  258. SDL_RenderClear(renderer);
  259. int triangleCount = arrayGetSize(projectedTriangles);
  260. for(i = 0; i < triangleCount; i++)
  261. {
  262. switch(currentDisplayMode)
  263. {
  264. case DISPLAY_MODE_SOLID:
  265. case DISPLAY_MODE_SOLID_WIRE:
  266. drawFilledTriangle(&projectedTriangles[i]);
  267. break;
  268. case DISPLAY_MODE_TEXTURED:
  269. case DISPLAY_MODE_TEXTURED_WIRE:
  270. drawTextureTriangle(&projectedTriangles[i]);
  271. break;
  272. default:
  273. break;
  274. }
  275. switch (currentDisplayMode)
  276. {
  277. case DISPLAY_MODE_SOLID_WIRE:
  278. case DISPLAY_MODE_TEXTURED_WIRE:
  279. case DISPLAY_MODE_WIREFRAME:
  280. drawTriangle(projectedTriangles[i].points[0].x, projectedTriangles[i].points[0].y,
  281. projectedTriangles[i].points[1].x, projectedTriangles[i].points[1].y,
  282. projectedTriangles[i].points[2].x, projectedTriangles[i].points[2].y,
  283. projectedTriangles[i].colour);
  284. break;
  285. default:
  286. break;
  287. }
  288. if (displayVertex)
  289. {
  290. int j;
  291. for(j = 0; j < 3; j++)
  292. {
  293. drawRectangle(projectedTriangles[i].points[j].x - 1,
  294. projectedTriangles[i].points[j].y - 1,
  295. 3, 3,
  296. 0xFFFF0000);
  297. }
  298. }
  299. }
  300. renderFrameBuffer();
  301. clearFrameBuffer(MAKE_RGB(0, 0, 0));
  302. clearZBuffer();
  303. renderTime = (renderTime + (getMicroSecTime() - ticks) / 1000.) / 2.0;
  304. drawText(5, 5, 0x11FF22, "Wait time: %02.2f ms", waitTime);
  305. drawText(5, 17, 0x11FF22, "Update time: %02.2f ms", updateTime);
  306. drawText(5, 29, 0x11FF22, "Render time: %02.2f ms", renderTime);
  307. drawText(5, 41, 0x11FF22, "FPS: %.1f | dT: %.3f", 1000 / (waitTime + updateTime + renderTime), deltaTime);
  308. drawText(5, 53, 0x11FF22, "Triangles: %d / %d", triangleCount, trianglesTotal);
  309. drawText(5, 65, 0x11FF22, "Cull: %s | PeCo: %s | ZB: %s",
  310. doNotCull?"N":"Y", doPerpectiveCorrection?"Y":"N", doZBuffer?"Y":"N");
  311. SDL_RenderPresent(renderer);
  312. }
  313. int main(int argc, char *argv[])
  314. {
  315. int param_i;
  316. bool fullScreen = false;
  317. MAX_DEBUG_LEVEL = TLOG_DEBUG;
  318. Log(TLOG_ALWAYS, NULL, "Booting 3D Engine (version %s)!", VERSION);
  319. for (param_i = 1 ; (param_i < argc) && (argv[param_i][0] == '-') ; param_i++)
  320. {
  321. switch (argv[param_i][1])
  322. {
  323. default: exit(-1); /* Option not recognized */
  324. case 'f': fullScreen = true; break;
  325. case 'w': windowWidth = atoi(argv[++param_i]);
  326. case 'h': windowHeight = atoi(argv[++param_i]); break;
  327. #ifdef DYNA_LOG_LEVEL
  328. case 'l': MAX_DEBUG_LEVEL = atoi(argv[++param_i]); break;
  329. #endif
  330. case '-': goto no_more_params; /* Could use break, but this is more clear */
  331. }
  332. }
  333. no_more_params:
  334. isRunning = initialiseWindow(fullScreen);
  335. setup();
  336. while (isRunning)
  337. {
  338. processInput();
  339. update();
  340. render();
  341. }
  342. arrayFree(projectedTriangles);
  343. textureCleanup();
  344. meshFree();
  345. destroyWindow();
  346. return 0;
  347. }