2
0

test_octasphere.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. extern "C" {
  2. #include "describe.h"
  3. }
  4. #define PAR_OCTASPHERE_IMPLEMENTATION
  5. #include "par_octasphere.h"
  6. #define CGLTF_IMPLEMENTATION
  7. #define CGLTF_WRITE_IMPLEMENTATION
  8. #include "cgltf_write.h"
  9. static void generate_gltf(const char* gltf_path, const char* bin_path, int num_vertices,
  10. int num_indices, float minpos[3], float maxpos[3], bool unlit) {
  11. cgltf_image images[3] = {
  12. {(char*)"octasphere color", (char*)"octasphere_color.png"},
  13. {(char*)"octasphere orm", (char*)"octasphere_orm.png"},
  14. {(char*)"octasphere normal", (char*)"octasphere_normal.png"},
  15. };
  16. cgltf_image& color_image = images[0];
  17. cgltf_image& orm_image = images[1];
  18. cgltf_image& normal_image = images[2];
  19. cgltf_texture textures[3] = {
  20. {(char*)"octasphere color", &color_image},
  21. {(char*)"octasphere orm", &orm_image},
  22. {(char*)"octasphere normal", &normal_image},
  23. };
  24. cgltf_texture& color_texture = textures[0];
  25. cgltf_texture& orm_texture = textures[1];
  26. cgltf_texture& normal_texture = textures[2];
  27. cgltf_material materials[1] = {};
  28. cgltf_material& octasphere_material = materials[0];
  29. octasphere_material.name = (char*)"octamat";
  30. octasphere_material.alpha_cutoff = 0.5f;
  31. octasphere_material.has_pbr_metallic_roughness = true;
  32. octasphere_material.pbr_metallic_roughness = {
  33. .base_color_texture = {&color_texture, 0, 1.0f},
  34. .metallic_roughness_texture = {&orm_texture, 0, 1.0f},
  35. .base_color_factor = {1, 1, 1, 1},
  36. .metallic_factor = 1,
  37. .roughness_factor = 1,
  38. };
  39. octasphere_material.occlusion_texture = {&orm_texture, 0, 1.0f};
  40. octasphere_material.normal_texture = {&normal_texture, 0, 1.0f};
  41. if (unlit) {
  42. octasphere_material.unlit = true;
  43. octasphere_material.pbr_metallic_roughness.metallic_roughness_texture = {};
  44. octasphere_material.occlusion_texture = {};
  45. octasphere_material.normal_texture = {};
  46. }
  47. // GEOMETRY
  48. cgltf_buffer buffers[1] = {};
  49. cgltf_mesh meshes[1] = {};
  50. cgltf_buffer_view buffer_views[4] = {};
  51. cgltf_accessor accessors[4] = {};
  52. cgltf_attribute attributes[3] = {};
  53. cgltf_primitive prims[2] = {};
  54. cgltf_buffer& octasphere_buffer = buffers[0];
  55. octasphere_buffer.uri = (char*)bin_path;
  56. octasphere_buffer.size = num_vertices * 32 + num_indices * 2;
  57. buffer_views[0].buffer = &octasphere_buffer;
  58. buffer_views[0].size = num_vertices * sizeof(float) * 3;
  59. buffer_views[0].type = cgltf_buffer_view_type_vertices;
  60. buffer_views[1].buffer = &octasphere_buffer;
  61. buffer_views[1].size = num_vertices * sizeof(float) * 3;
  62. buffer_views[1].offset = buffer_views[0].offset;
  63. buffer_views[1].offset += buffer_views[0].size;
  64. buffer_views[1].type = cgltf_buffer_view_type_vertices;
  65. buffer_views[2].buffer = &octasphere_buffer;
  66. buffer_views[2].size = num_vertices * sizeof(float) * 2;
  67. buffer_views[2].offset = buffer_views[1].offset;
  68. buffer_views[2].offset += buffer_views[1].size;
  69. buffer_views[2].type = cgltf_buffer_view_type_vertices;
  70. buffer_views[3].buffer = &octasphere_buffer;
  71. buffer_views[3].size = num_indices * sizeof(uint16_t);
  72. buffer_views[3].offset = buffer_views[2].offset;
  73. buffer_views[3].offset += buffer_views[2].size;
  74. buffer_views[3].type = cgltf_buffer_view_type_indices;
  75. accessors[0].buffer_view = &buffer_views[0];
  76. accessors[0].component_type = cgltf_component_type_r_32f;
  77. accessors[0].type = cgltf_type_vec3;
  78. accessors[0].count = num_vertices;
  79. accessors[0].has_min = accessors[0].has_max = true;
  80. for (int i = 0; i < 3; i++) {
  81. accessors[0].min[i] = minpos[i];
  82. accessors[0].max[i] = maxpos[i];
  83. }
  84. accessors[1].buffer_view = &buffer_views[1];
  85. accessors[1].component_type = cgltf_component_type_r_32f;
  86. accessors[1].type = cgltf_type_vec3;
  87. accessors[1].count = num_vertices;
  88. accessors[2].buffer_view = &buffer_views[2];
  89. accessors[2].component_type = cgltf_component_type_r_32f;
  90. accessors[2].type = cgltf_type_vec2;
  91. accessors[2].count = num_vertices;
  92. accessors[3].buffer_view = &buffer_views[3];
  93. accessors[3].component_type = cgltf_component_type_r_16u;
  94. accessors[3].type = cgltf_type_scalar;
  95. accessors[3].count = num_indices;
  96. // ATTRIBUTES
  97. attributes[0].name = (char*)"POSITION";
  98. attributes[0].data = &accessors[0];
  99. attributes[0].type = cgltf_attribute_type_position;
  100. attributes[1].name = (char*)"NORMAL";
  101. attributes[1].data = &accessors[1];
  102. attributes[1].type = cgltf_attribute_type_normal;
  103. attributes[2].name = (char*)"TEXCOORD_0";
  104. attributes[2].data = &accessors[2];
  105. attributes[2].type = cgltf_attribute_type_texcoord;
  106. // PRIMITIVES AND MESHES
  107. cgltf_primitive& prim = prims[0];
  108. prim.type = cgltf_primitive_type_triangles;
  109. prim.attributes = attributes;
  110. prim.attributes_count = 3;
  111. prim.indices = &accessors[3];
  112. prim.material = &octasphere_material;
  113. cgltf_mesh& mesh = meshes[0];
  114. mesh.name = (char*)"octasphere mesh";
  115. mesh.primitives = &prim;
  116. mesh.primitives_count = 1;
  117. // NODE HIERARCHY
  118. cgltf_node nodes[2];
  119. cgltf_node* pnodes[2] = {&nodes[0], &nodes[1]};
  120. nodes[0] = {
  121. .name = (char*)"root",
  122. .parent = {},
  123. .children = &pnodes[1],
  124. .children_count = 1,
  125. .skin = {},
  126. .mesh = {},
  127. .camera = {},
  128. .light = {},
  129. .weights = {},
  130. .weights_count = 0,
  131. .has_translation = false,
  132. .has_rotation = false,
  133. .has_scale = false,
  134. .has_matrix = true,
  135. .translation = {},
  136. .rotation = {},
  137. .scale = {},
  138. .matrix =
  139. {
  140. 1,
  141. 0,
  142. 0,
  143. 0,
  144. 0,
  145. 1,
  146. 0,
  147. 0,
  148. 0,
  149. 0,
  150. 1,
  151. 0,
  152. 0,
  153. 0,
  154. 0,
  155. 1,
  156. },
  157. };
  158. nodes[1] = {.name = (char*)"octasphere",
  159. .parent = {},
  160. .children = {},
  161. .children_count = 0,
  162. .skin = {},
  163. .mesh = &meshes[0]};
  164. cgltf_scene scene = {};
  165. scene.nodes = pnodes;
  166. scene.nodes_count = 1; // only one root
  167. // ASSET
  168. cgltf_data asset = {};
  169. asset.file_type = cgltf_file_type_gltf;
  170. asset.asset.generator = (char*)"test_octasphere";
  171. asset.asset.version = (char*)"2.0";
  172. asset.buffers = buffers;
  173. asset.buffers_count = sizeof(buffers) / sizeof(buffers[0]);
  174. asset.buffer_views = buffer_views;
  175. asset.buffer_views_count = sizeof(buffer_views) / sizeof(buffer_views[0]);
  176. asset.accessors = accessors;
  177. asset.accessors_count = sizeof(accessors) / sizeof(accessors[0]);
  178. asset.meshes = meshes;
  179. asset.meshes_count = sizeof(meshes) / sizeof(meshes[0]);
  180. asset.materials = materials;
  181. asset.materials_count = sizeof(materials) / sizeof(materials[0]);
  182. asset.images = images;
  183. asset.images_count = sizeof(images) / sizeof(images[0]);
  184. asset.textures = textures;
  185. asset.textures_count = sizeof(textures) / sizeof(textures[0]);
  186. asset.nodes = nodes;
  187. asset.nodes_count = sizeof(nodes) / sizeof(nodes[0]);
  188. asset.scenes = &scene;
  189. asset.scenes_count = 1;
  190. cgltf_options options = {};
  191. cgltf_write_file(&options, gltf_path, &asset);
  192. }
  193. int main() {
  194. describe("octasphere generator") {
  195. it("should generate a tile-like shape") {
  196. par_octasphere_config config = {
  197. .corner_radius = 0.1,
  198. .width = 1.2,
  199. .height = 1.2,
  200. .depth = 0.3,
  201. .num_subdivisions = 2,
  202. .uv_mode = PAR_OCTASPHERE_UV_LATLONG,
  203. };
  204. uint32_t indices_per_tile, vertices_per_tile;
  205. par_octasphere_get_counts(&config, &indices_per_tile, &vertices_per_tile);
  206. par_octasphere_mesh octasphere = {};
  207. octasphere.positions = (float*)malloc(vertices_per_tile * 12);
  208. octasphere.normals = (float*)malloc(vertices_per_tile * 12);
  209. octasphere.texcoords = (float*)malloc(vertices_per_tile * 8);
  210. octasphere.indices = (uint16_t*)malloc(indices_per_tile * 2);
  211. par_octasphere_populate(&config, &octasphere);
  212. uint32_t nindices, nvertices;
  213. par_octasphere_get_counts(&config, &nindices, &nvertices);
  214. assert_equal(nindices, indices_per_tile);
  215. assert_equal(nvertices, vertices_per_tile);
  216. free(octasphere.positions);
  217. free(octasphere.normals);
  218. free(octasphere.texcoords);
  219. free(octasphere.indices);
  220. }
  221. it("should generate rounded cube into glTF + bin files") {
  222. par_octasphere_config config = {
  223. .corner_radius = 0.4,
  224. .width = 1.2,
  225. .height = 1.2,
  226. .depth = 1.2,
  227. .num_subdivisions = 4,
  228. .uv_mode = PAR_OCTASPHERE_UV_PTEX,
  229. };
  230. uint32_t num_indices;
  231. uint32_t num_vertices;
  232. par_octasphere_get_counts(&config, &num_indices, &num_vertices);
  233. par_octasphere_mesh octasphere = {};
  234. octasphere.positions = (float*)malloc(num_vertices * 12);
  235. octasphere.normals = (float*)malloc(num_vertices * 12);
  236. octasphere.texcoords = (float*)malloc(num_vertices * 8);
  237. octasphere.indices = (uint16_t*)malloc(num_indices * 2);
  238. par_octasphere_populate(&config, &octasphere);
  239. float minpos[3] = {99, 99, 99};
  240. float maxpos[3] = {-99, -99, -99};
  241. float* ppos = octasphere.positions;
  242. for (int i = 0; i < num_vertices; i++, ppos += 3) {
  243. minpos[0] = PARO_MIN(minpos[0], ppos[0]);
  244. minpos[1] = PARO_MIN(minpos[1], ppos[1]);
  245. minpos[2] = PARO_MIN(minpos[2], ppos[2]);
  246. maxpos[0] = PARO_MAX(maxpos[0], ppos[0]);
  247. maxpos[1] = PARO_MAX(maxpos[1], ppos[1]);
  248. maxpos[2] = PARO_MAX(maxpos[2], ppos[2]);
  249. }
  250. FILE* file = fopen("octasphere.bin", "wb");
  251. if (!file) {
  252. puts("unable to open bin file for writing");
  253. exit(1);
  254. }
  255. fwrite(octasphere.positions, 12, num_vertices, file);
  256. fwrite(octasphere.normals, 12, num_vertices, file);
  257. fwrite(octasphere.texcoords, 8, num_vertices, file);
  258. fwrite(octasphere.indices, 2, num_indices, file);
  259. fclose(file);
  260. free(octasphere.positions);
  261. free(octasphere.normals);
  262. free(octasphere.texcoords);
  263. free(octasphere.indices);
  264. generate_gltf("octasphere.gltf", "octasphere.bin", num_vertices, num_indices, minpos,
  265. maxpos, true);
  266. }
  267. }
  268. return assert_failures();
  269. }