main.c 14 KB

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