main.c 14 KB

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