2
0

test_octasphere.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  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]) {
  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. // GEOMETRY
  42. cgltf_buffer buffers[1] = {};
  43. cgltf_mesh meshes[1] = {};
  44. cgltf_buffer_view buffer_views[4] = {};
  45. cgltf_accessor accessors[4] = {};
  46. cgltf_attribute attributes[3] = {};
  47. cgltf_primitive prims[2] = {};
  48. cgltf_buffer& octasphere_buffer = buffers[0];
  49. octasphere_buffer.uri = (char*) bin_path;
  50. octasphere_buffer.size = num_vertices * 32 + num_indices * 2;
  51. buffer_views[0].buffer = &octasphere_buffer;
  52. buffer_views[0].size = num_vertices * sizeof(float) * 3;
  53. buffer_views[0].type = cgltf_buffer_view_type_vertices;
  54. buffer_views[1].buffer = &octasphere_buffer;
  55. buffer_views[1].size = num_vertices * sizeof(float) * 3;
  56. buffer_views[1].offset = buffer_views[0].offset;
  57. buffer_views[1].offset += buffer_views[0].size;
  58. buffer_views[1].type = cgltf_buffer_view_type_vertices;
  59. buffer_views[2].buffer = &octasphere_buffer;
  60. buffer_views[2].size = num_vertices * sizeof(float) * 2;
  61. buffer_views[2].offset = buffer_views[1].offset;
  62. buffer_views[2].offset += buffer_views[1].size;
  63. buffer_views[2].type = cgltf_buffer_view_type_vertices;
  64. buffer_views[3].buffer = &octasphere_buffer;
  65. buffer_views[3].size = num_indices * sizeof(uint16_t);
  66. buffer_views[3].offset = buffer_views[2].offset;
  67. buffer_views[3].offset += buffer_views[2].size;
  68. buffer_views[3].type = cgltf_buffer_view_type_indices;
  69. accessors[0].buffer_view = &buffer_views[0];
  70. accessors[0].component_type = cgltf_component_type_r_32f;
  71. accessors[0].type = cgltf_type_vec3;
  72. accessors[0].count = num_vertices;
  73. accessors[0].has_min = accessors[0].has_max = true;
  74. for (int i = 0; i < 3; i++) {
  75. accessors[0].min[i] = minpos[i];
  76. accessors[0].max[i] = maxpos[i];
  77. }
  78. accessors[1].buffer_view = &buffer_views[1];
  79. accessors[1].component_type = cgltf_component_type_r_32f;
  80. accessors[1].type = cgltf_type_vec3;
  81. accessors[1].count = num_vertices;
  82. accessors[2].buffer_view = &buffer_views[2];
  83. accessors[2].component_type = cgltf_component_type_r_32f;
  84. accessors[2].type = cgltf_type_vec2;
  85. accessors[2].count = num_vertices;
  86. accessors[3].buffer_view = &buffer_views[3];
  87. accessors[3].component_type = cgltf_component_type_r_16u;
  88. accessors[3].type = cgltf_type_scalar;
  89. accessors[3].count = num_indices;
  90. // ATTRIBUTES
  91. attributes[0].name = (char*)"POSITION";
  92. attributes[0].data = &accessors[0];
  93. attributes[0].type = cgltf_attribute_type_position;
  94. attributes[1].name = (char*)"NORMAL";
  95. attributes[1].data = &accessors[1];
  96. attributes[1].type = cgltf_attribute_type_normal;
  97. attributes[2].name = (char*)"TEXCOORD_0";
  98. attributes[2].data = &accessors[2];
  99. attributes[2].type = cgltf_attribute_type_texcoord;
  100. // PRIMITIVES AND MESHES
  101. cgltf_primitive& prim = prims[0];
  102. prim.type = cgltf_primitive_type_triangles;
  103. prim.attributes = attributes;
  104. prim.attributes_count = 3;
  105. prim.indices = &accessors[3];
  106. prim.material = &octasphere_material;
  107. cgltf_mesh& mesh = meshes[0];
  108. mesh.name = (char*)"octasphere mesh";
  109. mesh.primitives = &prim;
  110. mesh.primitives_count = 1;
  111. // NODE HIERARCHY
  112. cgltf_node nodes[2];
  113. cgltf_node* pnodes[2] = {&nodes[0], &nodes[1]};
  114. nodes[0] = {
  115. .name = (char*)"root",
  116. .parent = {},
  117. .children = &pnodes[1],
  118. .children_count = 1,
  119. .skin = {},
  120. .mesh = {},
  121. .camera = {},
  122. .light = {},
  123. .weights = {},
  124. .weights_count = 0,
  125. .has_translation = false,
  126. .has_rotation = false,
  127. .has_scale = false,
  128. .has_matrix = true,
  129. .translation = {},
  130. .rotation = {},
  131. .scale = {},
  132. .matrix = {
  133. 1, 0, 0, 0,
  134. 0, 1, 0, 0,
  135. 0, 0, 1, 0,
  136. 0, 0, 0, 1,
  137. },
  138. };
  139. nodes[1] = {
  140. .name = (char*) "octasphere",
  141. .parent = {},
  142. .children = {},
  143. .children_count = 0,
  144. .skin = {},
  145. .mesh = &meshes[0]
  146. };
  147. cgltf_scene scene = {};
  148. scene.nodes = pnodes;
  149. scene.nodes_count = 1; // only one root
  150. // ASSET
  151. cgltf_data asset = {};
  152. asset.file_type = cgltf_file_type_gltf;
  153. asset.asset.generator = (char*)"test_octasphere";
  154. asset.asset.version = (char*)"2.0";
  155. asset.buffers = buffers;
  156. asset.buffers_count = sizeof(buffers) / sizeof(buffers[0]);
  157. asset.buffer_views = buffer_views;
  158. asset.buffer_views_count = sizeof(buffer_views) / sizeof(buffer_views[0]);
  159. asset.accessors = accessors;
  160. asset.accessors_count = sizeof(accessors) / sizeof(accessors[0]);
  161. asset.meshes = meshes;
  162. asset.meshes_count = sizeof(meshes) / sizeof(meshes[0]);
  163. asset.materials = materials;
  164. asset.materials_count = sizeof(materials) / sizeof(materials[0]);
  165. asset.images = images;
  166. asset.images_count = sizeof(images) / sizeof(images[0]);
  167. asset.textures = textures;
  168. asset.textures_count = sizeof(textures) / sizeof(textures[0]);
  169. asset.nodes = nodes;
  170. asset.nodes_count = sizeof(nodes) / sizeof(nodes[0]);
  171. asset.scenes = &scene;
  172. asset.scenes_count = 1;
  173. cgltf_options options = {};
  174. cgltf_write_file(&options, gltf_path, &asset);
  175. }
  176. int main()
  177. {
  178. describe("octasphere generator") {
  179. it("should generate a tile-like shape") {
  180. par_octasphere_config config = {
  181. .corner_radius = 0.1,
  182. .width = 1.2,
  183. .height = 1.2,
  184. .depth = 0.3,
  185. .num_subdivisions = 2,
  186. .uv_mode = PAR_OCTASPHERE_UV_LATLONG,
  187. };
  188. uint32_t indices_per_tile;
  189. uint32_t vertices_per_tile;
  190. par_octasphere_get_counts(&config, &indices_per_tile, &vertices_per_tile);
  191. par_octasphere_mesh octasphere = {};
  192. octasphere.positions = (float*)malloc(vertices_per_tile * 12);
  193. octasphere.normals = (float*)malloc(vertices_per_tile * 12);
  194. octasphere.texcoords = (float*)malloc(vertices_per_tile * 8);
  195. octasphere.indices = (uint16_t*)malloc(indices_per_tile * 2);
  196. par_octasphere_populate(&config, &octasphere);
  197. free(octasphere.positions);
  198. free(octasphere.normals);
  199. free(octasphere.texcoords);
  200. free(octasphere.indices);
  201. }
  202. it("should generate rounded cube into glTF + bin files") {
  203. par_octasphere_config config = {
  204. .corner_radius = 0.4,
  205. .width = 1.2,
  206. .height = 1.2,
  207. .depth = 1.2,
  208. .num_subdivisions = 3,
  209. .uv_mode = PAR_OCTASPHERE_UV_LATLONG,
  210. };
  211. uint32_t num_indices;
  212. uint32_t num_vertices;
  213. par_octasphere_get_counts(&config, &num_indices, &num_vertices);
  214. par_octasphere_mesh octasphere = {};
  215. octasphere.positions = (float*)malloc(num_vertices * 12);
  216. octasphere.normals = (float*)malloc(num_vertices * 12);
  217. octasphere.texcoords = (float*)malloc(num_vertices * 8);
  218. octasphere.indices = (uint16_t*)malloc(num_indices * 2);
  219. par_octasphere_populate(&config, &octasphere);
  220. float minpos[3] = { 99, 99, 99};
  221. float maxpos[3] = {-99, -99, -99};
  222. float* ppos = octasphere.positions;
  223. for (int i = 0; i < num_vertices; i++, ppos += 3) {
  224. minpos[0] = PARO_MIN(minpos[0], ppos[0]);
  225. minpos[1] = PARO_MIN(minpos[1], ppos[1]);
  226. minpos[2] = PARO_MIN(minpos[2], ppos[2]);
  227. maxpos[0] = PARO_MAX(maxpos[0], ppos[0]);
  228. maxpos[1] = PARO_MAX(maxpos[1], ppos[1]);
  229. maxpos[2] = PARO_MAX(maxpos[2], ppos[2]);
  230. }
  231. FILE* file = fopen("octasphere.bin", "wb");
  232. if (!file) {
  233. puts("unable to open bin file for writing");
  234. exit(1);
  235. }
  236. fwrite(octasphere.positions, 12, num_vertices, file);
  237. fwrite(octasphere.normals, 12, num_vertices, file);
  238. fwrite(octasphere.texcoords, 8, num_vertices, file);
  239. fwrite(octasphere.indices, 2, num_indices, file);
  240. fclose(file);
  241. free(octasphere.positions);
  242. free(octasphere.normals);
  243. free(octasphere.texcoords);
  244. free(octasphere.indices);
  245. generate_gltf("octasphere.gltf", "octasphere.bin", num_vertices, num_indices,
  246. minpos, maxpos);
  247. }
  248. }
  249. return assert_failures();
  250. }