main.c 10 KB

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