main.c 14 KB

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