main.c 12 KB

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