main.c 13 KB

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