main.c 9.2 KB

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