main.c 11 KB

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