main.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741
  1. /*
  2. * viewer/main.c
  3. *
  4. * Copyright (C) 2022 bzt (bztsrc@gitlab)
  5. *
  6. * Permission is hereby granted, free of charge, to any person
  7. * obtaining a copy of this software and associated documentation
  8. * files (the "Software"), to deal in the Software without
  9. * restriction, including without limitation the rights to use, copy,
  10. * modify, merge, publish, distribute, sublicense, and/or sell copies
  11. * of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be
  15. * included in all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  19. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  21. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  24. * DEALINGS IN THE SOFTWARE.
  25. *
  26. * @brief WebAssembly based Model3D viewer
  27. * https://gitlab.com/bztsrc/model3d
  28. *
  29. */
  30. #include <math.h>
  31. #include <emscripten.h>
  32. #include <emscripten/html5.h>
  33. #include <emscripten/key_codes.h>
  34. #include <GLES3/gl3.h>
  35. #include <EGL/egl.h>
  36. #define M3D_IMPLEMENTATION
  37. #define M3D_ASCII
  38. #include <m3d.h>
  39. int msec = 0, screenw, screenh;
  40. GLuint frag_id, vert_id, prog_id, proj_id, view_id, vao_id, vbo_id;
  41. GLuint tex_id, ht_id, amb_id, spe_id, shi_id, emi_id;
  42. float proj[16], view[16];
  43. const char *frag = "#version 300 es\n"
  44. "precision mediump float;\n"
  45. "in vec3 pos;\n"
  46. "in vec3 norm;\n"
  47. "in vec2 uv;\n"
  48. "in vec4 col;\n"
  49. "in mat4 mv;\n"
  50. "out vec4 glColor;\n"
  51. "uniform sampler2D tex;\n"
  52. "uniform int hastex;\n"
  53. "uniform vec4 ambient;\n"
  54. "uniform vec4 specular;\n"
  55. "uniform float shininess;\n"
  56. "uniform vec4 emission;\n"
  57. "vec3 light_position = vec3(-1.0, 2.0, 2.0);\n"
  58. "vec3 light_color = vec3(1.0, 1.0, 1.0);\n"
  59. "void main(){\n"
  60. "vec3 light_dir = normalize(light_position - pos);\n"
  61. "vec4 diff = vec4(max(dot(norm, light_dir), 0.0) * light_color, 1.0);\n"
  62. "vec3 eyedirn = normalize(vec3(0.0) - pos);\n"
  63. "vec3 refl_dir = reflect(-light_dir, norm);\n"
  64. "vec4 spec = vec4(pow(max(dot(eyedirn, refl_dir), 0.0), shininess) * light_color, 1.0) * specular;\n"
  65. "vec4 color;\n"
  66. "if(hastex != 0) color = texture(tex, uv); else color = col;\n"
  67. "glColor = (ambient + diff + spec) * color;\n"
  68. "}";
  69. const char *vert = "#version 300 es\n"
  70. "layout(location = 0) in vec3 vert_pos;\n"
  71. "layout(location = 1) in vec3 vert_norm;\n"
  72. "layout(location = 2) in vec2 vert_uv;\n"
  73. "layout(location = 3) in vec4 vert_col;\n"
  74. "uniform mat4 proj;\n"
  75. "uniform mat4 view;\n"
  76. "out vec3 pos;\n"
  77. "out vec3 norm;\n"
  78. "out vec2 uv;\n"
  79. "out vec4 col;\n"
  80. "out mat4 mv;\n"
  81. "void main(){\n"
  82. "gl_Position = proj * view * vec4(vert_pos, 1.0f);\n"
  83. "pos = vert_pos;\n"
  84. "norm = normalize(mat3(transpose(inverse(view))) * vert_norm);\n"
  85. "uv = vert_uv;\n"
  86. "col = vert_col;\n"
  87. "mv = view;\n"
  88. "}";
  89. /*
  90. void debug_shader(GLuint shader)
  91. {
  92. GLint success = 0, log_size = 0;
  93. char *log;
  94. glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
  95. if(!success) {
  96. glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_size);
  97. log = malloc(log_size);
  98. glGetShaderInfoLog(shader, log_size, NULL, log);
  99. printf("%s\n", log);
  100. free(log);
  101. exit(1);
  102. }
  103. }
  104. */
  105. unsigned int actionid = 0, frame = -1U, fpsdivisor = 1, lastframe = 0;
  106. char *wintitle = NULL, infostr[4096];
  107. m3d_t *model = NULL;
  108. int mousebtn = 0, mousemove = 0, mousex = 0, mousey = 0, mousez = 0, doframe = 0, domesh = 1, doskel = 0;
  109. float mindist = 1, maxdist = 25, distance = 5.5, pitch = /*0*/-35.264, yaw = /*0*/45;
  110. unsigned int numtexture, texture[32];
  111. unsigned char checker_data[4*128*128];
  112. uint32_t default_color = 0xFF235580;
  113. /* vertex attributes. Packing shouldn't be an issue, but be safe than sorry */
  114. typedef struct {
  115. float vertex_x;
  116. float vertex_y;
  117. float vertex_z;
  118. float normal_x;
  119. float normal_y;
  120. float normal_z;
  121. float tex_u;
  122. float tex_v;
  123. uint32_t color;
  124. uint32_t vertexid; /* needed for animation */
  125. uint32_t normalid;
  126. } __attribute__((packed))vbo_t;
  127. vbo_t *vbo = NULL;
  128. /* integer triplets, material index, vbo index and vbo size */
  129. unsigned int numvbo, numvboidx = 0, *vboidx = NULL;
  130. /**
  131. * Clean up on exit
  132. */
  133. void cleanup()
  134. {
  135. if(model) m3d_free(model);
  136. if(vbo) free(vbo);
  137. }
  138. /**
  139. * Print an error message and quit
  140. */
  141. void error(char *msg)
  142. {
  143. fprintf(stderr, "m3dview: %s\n", msg);
  144. EM_ASM({var msg = new TextDecoder("utf-8").decode(new Uint8Array(Module.HEAPU8.buffer,$0,$1));alert(msg);}, msg, strlen(msg));
  145. cleanup();
  146. exit(1);
  147. }
  148. /**
  149. * Initialize loaded model
  150. */
  151. void initmodel()
  152. {
  153. unsigned int i, j, k, l, last = -2U;
  154. uint32_t diffuse_color = default_color;
  155. sprintf(infostr, "%s (%s, %s)\n%d triangles, %d vertices (%d bit), scale %g, %d actions",
  156. model->name, model->license[0] ? model->license : "no license", model->author[0] ? model->author : "no author",
  157. model->numface, model->numvertex, model->vc_s << 3, model->scale, model->numaction);
  158. EM_ASM({var txt = new TextDecoder("utf-8").decode(new Uint8Array(Module.HEAPU8.buffer,$0,$1));document.getElementById('info').innerHTML=txt;},
  159. infostr, strlen(infostr));
  160. /* count how many times we switch material context */
  161. numvbo = model->numface * 3;
  162. for(i = 0; i < model->numface; i++) {
  163. if(last != model->face[i].materialid) {
  164. last = model->face[i].materialid;
  165. numvboidx++;
  166. }
  167. }
  168. vboidx = (unsigned int*)malloc(numvboidx * 3 * sizeof(numvboidx));
  169. if(!vboidx) error("unable to allocate memory");
  170. /* set up vbo array */
  171. vbo = (vbo_t*)malloc(numvbo * sizeof(vbo_t));
  172. if(!vbo) error("unable to allocate memory");
  173. memset(vbo, 0, numvbo * sizeof(vbo_t));
  174. for(i = k = l = 0, last = -2U; i < model->numface; i++) {
  175. /* if we change material, record it in vboidx and set diffuse color in vbo.color */
  176. if(last != model->face[i].materialid) {
  177. last = model->face[i].materialid;
  178. diffuse_color = default_color;
  179. if(last < model->nummaterial)
  180. for(j = 0; j < model->material[last].numprop; j++)
  181. if(model->material[last].prop[j].type == m3dp_Kd) {
  182. diffuse_color = model->material[last].prop[j].value.color;
  183. break;
  184. }
  185. if(l)
  186. vboidx[l-1] = k - vboidx[l-2];
  187. vboidx[l] = last;
  188. vboidx[l + 1] = k;
  189. l += 3;
  190. }
  191. for(j = 0; j < 3; j++, k++) {
  192. /* fill up VBO records */
  193. memcpy(&vbo[k].vertex_x, &model->vertex[model->face[i].vertex[j]].x, 3 * sizeof(float));
  194. memcpy(&vbo[k].normal_x, &model->vertex[model->face[i].normal[j]].x, 3 * sizeof(float));
  195. if(model->tmap && model->face[i].texcoord[j] < model->numtmap) {
  196. vbo[k].tex_u = model->tmap[model->face[i].texcoord[j]].u;
  197. vbo[k].tex_v = 1.0f - model->tmap[model->face[i].texcoord[j]].v;
  198. } else
  199. vbo[k].tex_u = vbo[k].tex_v = 0.0f;
  200. /* if there's no material, use vertex color for vbo.color, may change for every vertex */
  201. vbo[k].color = (model->face[i].materialid != -1U ? diffuse_color : (
  202. model->vertex[model->face[i].vertex[j]].color ? model->vertex[model->face[i].vertex[j]].color : default_color));
  203. if(!vbo[k].color) vbo[k].color = default_color;
  204. vbo[k].vertexid = model->face[i].vertex[j];
  205. vbo[k].normalid = model->face[i].normal[j];
  206. }
  207. }
  208. if(l)
  209. vboidx[l-1] = k - vboidx[l-2];
  210. /* set up GL textures */
  211. #ifdef GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
  212. glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, (GLint*)&numtexture);
  213. if(numtexture > 31) numtexture = 31;
  214. if(numtexture < 1) return;
  215. #else
  216. numtexture = 32;
  217. #endif
  218. memset(texture, 0, sizeof(texture));
  219. glGenTextures(numtexture, (GLuint*)&texture);
  220. for (j = k = 0; j < 128; j++)
  221. for (i = 0; i < 128; i++, k += 4) {
  222. memcpy(&checker_data[k], &default_color, 4);
  223. checker_data[k] += ((((i>>5) & 1) ^ ((j>>5) & 1)) << 5);
  224. }
  225. glBindTexture(GL_TEXTURE_2D, texture[0]);
  226. #ifdef GL_GENERATE_MIPMAP
  227. glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
  228. #endif
  229. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  230. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  231. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  232. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  233. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, checker_data);
  234. /* add textures from model */
  235. if(model->numtexture) {
  236. for(i = 0; i < numtexture && i < model->numtexture; i++) {
  237. if(!model->texture[i].w || !model->texture[i].h || !model->texture[i].d) {
  238. fprintf(stderr, "m3dview: unable to load texture '%s'\n", model->texture[i].name);
  239. texture[1 + i] = texture[0];
  240. continue;
  241. }
  242. glBindTexture(GL_TEXTURE_2D, texture[1 + i]);
  243. #ifdef GL_GENERATE_MIPMAP
  244. glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
  245. #endif
  246. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  247. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  248. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  249. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  250. switch(model->texture[i].f) {
  251. case 1: k = GL_LUMINANCE; break;
  252. case 2: k = GL_LUMINANCE_ALPHA; break;
  253. case 3: k = GL_RGB; break;
  254. default: k = GL_RGBA; break;
  255. }
  256. glTexImage2D(GL_TEXTURE_2D, 0, k, model->texture[i].w, model->texture[i].h, 0, k, GL_UNSIGNED_BYTE,
  257. model->texture[i].d);
  258. }
  259. }
  260. }
  261. /**
  262. * Multiply a vertex with a transformation matrix
  263. */
  264. void vec3_mul_mat4(m3dv_t *out, m3dv_t *v, float *mat)
  265. {
  266. out->x = mat[ 0] * v->x + mat[ 1] * v->y + mat[ 2] * v->z + mat[ 3];
  267. out->y = mat[ 4] * v->x + mat[ 5] * v->y + mat[ 6] * v->z + mat[ 7];
  268. out->z = mat[ 8] * v->x + mat[ 9] * v->y + mat[10] * v->z + mat[11];
  269. }
  270. /**
  271. * Multiply a vertex with a rotation matrix
  272. */
  273. void vec3_mul_mat3(m3dv_t *out, m3dv_t *v, float *mat)
  274. {
  275. out->x = mat[ 0] * v->x + mat[ 1] * v->y + mat[ 2] * v->z;
  276. out->y = mat[ 4] * v->x + mat[ 5] * v->y + mat[ 6] * v->z;
  277. out->z = mat[ 8] * v->x + mat[ 9] * v->y + mat[10] * v->z;
  278. }
  279. /**
  280. * Multiply 4x4 matrix to the left
  281. */
  282. void mat4_mul(float *c, float *b) {
  283. int i, j, k;
  284. float s, a[16];
  285. memcpy(&a, c, sizeof(a));
  286. for(i = 0; i < 4; i++)
  287. for(j = 0; j < 4; j++) {
  288. for(s = 0.0, k = 0; k < 4; k++)
  289. s += a[i+k*4] * b[k+j*4];
  290. c[i+j*4] = s;
  291. }
  292. }
  293. /**
  294. * Rotate by angle t on axis u
  295. */
  296. void rotate(float *out, float t, int u)
  297. {
  298. float s, c, a[16];
  299. int v, w;
  300. if((v = u + 1) > 2) v = 0;
  301. if((w = v + 1) > 2) w = 0;
  302. t *= 0.0174533; s = sinf(t); c = cosf(t);
  303. memset(&a, 0, sizeof(a)); a[0] = a[5] = a[10] = a[15] = 1.0;
  304. a[v+v*4] = c;
  305. a[v+w*4] = -s;
  306. a[w+v*4] = s;
  307. a[w+w*4] = c;
  308. mat4_mul(out, a);
  309. }
  310. /**
  311. * Calculate frustum projection
  312. */
  313. void frustum(float *out, float l, float r, float b, float t, float n, float f)
  314. {
  315. out[0] = (2.0 * n) / (r - l); out[1] = 0; out[2] = 0; out[3] = 0;
  316. out[4] = 0; out[5] = (2.0 * n) / (t - b); out[6] = 0; out[7] = 0;
  317. out[8] = (r + l) / (r - l); out[9] = (t + b) / (t - b); out[10] = -(f + n) / (f - n); out[11] = -1;
  318. out[12] = 0; out[13] = 0; out[14] = -(2.0 * f * n) / (f - n); out[15] = 0;
  319. }
  320. /**
  321. * Convert a standard uint32_t color into OpenGL color
  322. */
  323. void set_color(uint32_t c, float *f) {
  324. if(!c) {
  325. f[0] = f[1] = f[2] = 0.0; f[3] = 1.0;
  326. } else {
  327. f[3] = ((float)((c >> 24)&0xff)) / 255;
  328. f[2] = ((float)((c >> 16)&0xff)) / 255;
  329. f[1] = ((float)((c >> 8)&0xff)) / 255;
  330. f[0] = ((float)((c >> 0)&0xff)) / 255;
  331. }
  332. }
  333. /**
  334. * Set material to use.
  335. */
  336. void set_material(unsigned int mi)
  337. {
  338. unsigned int i, t;
  339. m3dm_t *m;
  340. float ambient[4] = { 0 }, specular[4] = { 0 }, emission[4] = { 0 }, shininess = 0.0;
  341. glActiveTexture(GL_TEXTURE0);
  342. glUniform1i(ht_id, 0);
  343. /* update with what we have in this new material */
  344. if(mi < model->nummaterial) {
  345. m = &model->material[mi];
  346. for(i = 0; i < m->numprop; i++) {
  347. switch(m->prop[i].type) {
  348. case m3dp_Kd: /* this is already copied into the VBO */ break;
  349. case m3dp_Ka: set_color(m->prop[i].value.color, (float*)&ambient); break;
  350. case m3dp_Ks: set_color(m->prop[i].value.color, (float*)&specular); break;
  351. case m3dp_Ke: set_color(m->prop[i].value.color, (float*)&emission); break;
  352. case m3dp_Ns: shininess = m->prop[i].value.fnum; break;
  353. case m3dp_map_Kd:
  354. if(numtexture) {
  355. t = m->prop[i].value.textureid < numtexture ? m->prop[i].value.textureid + 1 : 0;
  356. glUniform1i(ht_id, 1);
  357. glUniform1i(tex_id, 0);
  358. glBindTexture(GL_TEXTURE_2D, texture[t]);
  359. }
  360. break;
  361. }
  362. }
  363. }
  364. glUniform4fv(amb_id, 1, ambient);
  365. glUniform4fv(spe_id, 1, specular);
  366. glUniform1f(shi_id, shininess);
  367. glUniform4fv(emi_id, 1, emission);
  368. }
  369. /**
  370. * Set FPS divisor for animation debugging
  371. */
  372. void fpsdiv(int idx)
  373. {
  374. switch(idx) {
  375. case 1: fpsdivisor = 2; break;
  376. case 2: fpsdivisor = 3; break;
  377. case 3: fpsdivisor = 5; break;
  378. case 4: fpsdivisor = 10; break;
  379. case 5: fpsdivisor = 15; break;
  380. case 6: fpsdivisor = 20; break;
  381. case 7: fpsdivisor = 25; break;
  382. case 8: fpsdivisor = 30; break;
  383. case 9: fpsdivisor = 60; break;
  384. default: fpsdivisor = 1; break;
  385. }
  386. }
  387. /**
  388. * Toggle continous playback
  389. */
  390. void continous(void)
  391. {
  392. doframe ^= 1;
  393. if(actionid < model->numaction) {
  394. if(frame == -1U) frame = model->action[actionid].numframe - 1;
  395. if(frame > model->action[actionid].numframe - 1) frame = 0;
  396. } else
  397. frame = 0;
  398. }
  399. /**
  400. * Switch to next frame
  401. */
  402. void nextframe(void)
  403. {
  404. doframe = 0; frame++;
  405. continous();
  406. }
  407. /**
  408. * Switch to previous frame
  409. */
  410. void prevframe(void)
  411. {
  412. doframe = 0; frame--;
  413. continous();
  414. }
  415. /**
  416. * Zoom in
  417. */
  418. void zoomin(void)
  419. {
  420. distance -= 0.01 * distance;
  421. if(distance < 0.000001) distance = 0.000001;
  422. }
  423. /**
  424. * Zoom out
  425. */
  426. void zoomout(void)
  427. {
  428. distance += 0.01 * distance;
  429. if(distance > 100000) distance = 100000;
  430. }
  431. /**
  432. * Animate the mesh
  433. */
  434. m3db_t *animate_model(unsigned int msec)
  435. {
  436. unsigned int i, j, s;
  437. m3db_t *animpose;
  438. m3dv_t tmp1, tmp2;
  439. if(actionid < model->numaction && doframe) {
  440. /* if we are frame debugging, use the exact timestamp of the frame as msec */
  441. if(frame == -1U) frame = 0;
  442. msec = model->action[actionid].frame[frame].msec;
  443. } else
  444. /* otherwise modify by the debugging fps divisor (is 1 by default) */
  445. msec /= fpsdivisor;
  446. /* get the animation-pose skeleton*/
  447. animpose = m3d_pose(model, actionid, msec);
  448. /* don't regenerate if we have the same timestamp as last time */
  449. if(msec == lastframe) return animpose;
  450. lastframe = msec;
  451. /* convert mesh vertices from bind-pose into animation-pose */
  452. for(i = 0; i < numvbo; i++) {
  453. s = model->vertex[vbo[i].vertexid].skinid;
  454. if(s != -1U) {
  455. /* we assume that vbo_t is packed and normals follow vertex coordinates */
  456. memset(&vbo[i].vertex_x, 0, 6 * sizeof(float));
  457. for(j = 0; j < M3D_NUMBONE && model->skin[s].weight[j] > 0.0; j++) {
  458. /* transfer from bind-pose model-space into bone-local space */
  459. vec3_mul_mat4(&tmp1, &model->vertex[vbo[i].vertexid], (float*)&model->bone[ model->skin[s].boneid[j] ].mat4);
  460. /* transfer from bone-local space into animation-pose model-space */
  461. vec3_mul_mat4(&tmp2, &tmp1, (float*)&animpose[ model->skin[s].boneid[j] ].mat4);
  462. /* multiply with weight and accumulate */
  463. vbo[i].vertex_x += tmp2.x * model->skin[s].weight[j];
  464. vbo[i].vertex_y += tmp2.y * model->skin[s].weight[j];
  465. vbo[i].vertex_z += tmp2.z * model->skin[s].weight[j];
  466. /* now again for the normal vector */
  467. vec3_mul_mat3(&tmp1, &model->vertex[vbo[i].normalid], (float*)&model->bone[ model->skin[s].boneid[j] ].mat4);
  468. vec3_mul_mat3(&tmp2, &tmp1, (float*)&animpose[ model->skin[s].boneid[j] ].mat4);
  469. vbo[i].normal_x += tmp2.x * model->skin[s].weight[j];
  470. vbo[i].normal_y += tmp2.y * model->skin[s].weight[j];
  471. vbo[i].normal_z += tmp2.z * model->skin[s].weight[j];
  472. }
  473. }
  474. }
  475. glBufferSubData(GL_ARRAY_BUFFER, 0, numvbo * sizeof(vbo_t), vbo);
  476. return animpose;
  477. }
  478. /**
  479. * Display the model
  480. */
  481. void display(unsigned int msec)
  482. {
  483. m3db_t *animpose = NULL, *bones;
  484. unsigned int i, j;
  485. float ct, cp, st, sp, tp[16], fov, fov2, *skel = NULL, s;
  486. char tmp[1024];
  487. /* handle model rotation */
  488. if(mousemove) {
  489. yaw -= mousex * 0.3;
  490. pitch -= mousey * 0.2;
  491. if (pitch < -90) pitch = -90;
  492. if (pitch > 90) pitch = 90;
  493. if (yaw < 0) yaw += 360;
  494. if (yaw > 360) yaw -= 360;
  495. mousemove = 0;
  496. }
  497. /* switch action to animate */
  498. if(actionid == -1U) actionid = model->numaction;
  499. else if((unsigned int)actionid > model->numaction) actionid = 0;
  500. /* animate the mesh */
  501. if(model->numaction)
  502. animpose = animate_model(msec);
  503. screenw = EM_ASM_INT({ return Module.canvas.width; });
  504. screenh = EM_ASM_INT({ return Module.canvas.height; });
  505. glViewport(0, 0, screenw, screenh);
  506. glUseProgram(prog_id);
  507. glBindVertexArray(vao_id);
  508. /* display model */
  509. glEnable(GL_DEPTH_TEST);
  510. glDepthFunc(GL_LESS);
  511. glClearColor(0.1, 0.1, 0.1, 1);
  512. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  513. fov = 0.221694 * (mindist/5);
  514. fov2 = fov * (float)screenw / (float)screenh;
  515. frustum(proj, -fov2, fov2, -fov, fov, mindist/5, maxdist*5);
  516. glUniformMatrix4fv(proj_id, 1, GL_FALSE, (GLfloat*)proj);
  517. view[ 0] = 1; view[ 1] = 0; view[ 2] = 0; view[ 3] = 0;
  518. view[ 4] = 0; view[ 5] = 1; view[ 6] = 0; view[ 7] = 0;
  519. view[ 8] = 0; view[ 9] = 0; view[10] = 1; view[11] = 0;
  520. view[12] = 0; view[13] = (float)mousez/10.0f; view[14] = -distance*1.1; view[15] = 1;
  521. rotate(view, -pitch, 0);
  522. rotate(view, -yaw, 1);
  523. glUniformMatrix4fv(view_id, 1, GL_FALSE, (GLfloat*)view);
  524. /* draw the skeleton */
  525. if(doskel && model->numbone) {
  526. bones = animpose ? animpose : model->bone;
  527. }
  528. if(animpose) {
  529. free(animpose);
  530. animpose = NULL;
  531. }
  532. /* draw the mesh */
  533. if(domesh) {
  534. for(i = j = 0; i < numvboidx; i++, j += 3) {
  535. set_material(vboidx[j + 0]);
  536. glDrawArrays(GL_TRIANGLES, vboidx[j + 1], vboidx[j + 2]);
  537. }
  538. }
  539. glDisable(GL_DEPTH_TEST);
  540. tmp[0] = 0;
  541. if(model->numaction) {
  542. sprintf(tmp, "current: %s ", actionid < model->numaction ? model->action[actionid].name : "(bind-pose)");
  543. }
  544. if(doframe) {
  545. sprintf(&tmp[strlen(tmp)], "frame %4d / %4d",
  546. actionid < model->numaction ? frame + 1 : 1,
  547. actionid < model->numaction ? model->action[actionid].numframe : 1);
  548. }
  549. sprintf(&tmp[strlen(tmp)], "\n");
  550. EM_ASM({var txt = new TextDecoder("utf-8").decode(new Uint8Array(Module.HEAPU8.buffer,$0,$1));document.getElementById('stat').innerHTML=txt;},
  551. tmp, strlen(tmp));
  552. }
  553. /**
  554. * Main display loop
  555. */
  556. static void main_loop(void)
  557. {
  558. msec += 1000/60;
  559. display(msec);
  560. }
  561. /**
  562. * Process a keyboard event callback
  563. */
  564. EM_BOOL key(int type, const EmscriptenKeyboardEvent *key, void *data)
  565. {
  566. int ourkey = 1;
  567. (void)type;
  568. (void)data;
  569. if(!key->ctrlKey && !key->shiftKey && !key->altKey && !key->metaKey) {
  570. switch(key->keyCode) {
  571. case DOM_VK_Q: case DOM_VK_ESCAPE: cleanup(); exit(1); break;
  572. case DOM_VK_UP: mousez++; break;
  573. case DOM_VK_DOWN: mousez--; break;
  574. case DOM_VK_LEFT: mousex = -10; mousey = 0; mousemove = 1; break;
  575. case DOM_VK_RIGHT: mousex = 10; mousey = 0; mousemove = 1; break;
  576. case DOM_VK_PAGE_UP: actionid--; break;
  577. case DOM_VK_TAB:
  578. case DOM_VK_PAGE_DOWN: actionid++; break;
  579. case DOM_VK_EQUALS: case DOM_VK_PLUS: case DOM_VK_ADD: zoomin(); break;
  580. case DOM_VK_HYPHEN_MINUS: case DOM_VK_SUBTRACT: zoomout(); break;
  581. case DOM_VK_COMMA: prevframe(); break;
  582. case DOM_VK_PERIOD: nextframe(); break;
  583. case DOM_VK_SPACE: continous(); break;
  584. case DOM_VK_M: domesh ^= 1; break;
  585. case DOM_VK_S: doskel ^= 1; break;
  586. case DOM_VK_0:
  587. case DOM_VK_1:
  588. case DOM_VK_2:
  589. case DOM_VK_3:
  590. case DOM_VK_4:
  591. case DOM_VK_5:
  592. case DOM_VK_6:
  593. case DOM_VK_7:
  594. case DOM_VK_8:
  595. case DOM_VK_9: fpsdiv(key->keyCode-DOM_VK_0); break;
  596. default: ourkey = 0; break;
  597. }
  598. if(ourkey) EM_ASM({event.preventDefault();event.stopPropagation();});
  599. }
  600. return 0;
  601. }
  602. /**
  603. * Process a mouse event callback
  604. */
  605. EM_BOOL mouse(int type, const EmscriptenMouseEvent *evt, void *data)
  606. {
  607. (void)type;
  608. (void)data;
  609. if(evt->buttons == 1) {
  610. mousex = evt->movementX;
  611. mousey = evt->movementY;
  612. mousemove = 1;
  613. }
  614. return 0;
  615. }
  616. /**
  617. * Process a wheel event callback
  618. */
  619. EM_BOOL wheel(int type, const EmscriptenWheelEvent *evt, void *data)
  620. {
  621. (void)type;
  622. (void)data;
  623. if(evt->deltaY < 0) zoomin(); else
  624. if(evt->deltaY > 0) zoomout(); else
  625. return 0;
  626. EM_ASM({event.preventDefault();event.stopPropagation();});
  627. return 0;
  628. }
  629. /**
  630. * Main procedure. Set up and main loop
  631. */
  632. int m3d_viewer(unsigned char *data, int size)
  633. {
  634. EmscriptenWebGLContextAttributes attrs;
  635. EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context;
  636. GLint status;
  637. double mx, my, px, py;
  638. emscripten_webgl_init_context_attributes(&attrs);
  639. attrs.majorVersion = 2;
  640. context = emscripten_webgl_create_context("canvas", &attrs);
  641. emscripten_webgl_make_context_current(context);
  642. frag_id = glCreateShader(GL_FRAGMENT_SHADER);
  643. glShaderSource(frag_id, 1, (const GLchar**)&frag, NULL);
  644. glCompileShader(frag_id);
  645. /*debug_shader(frag_id);*/
  646. status = 0; glGetShaderiv(frag_id, GL_COMPILE_STATUS, &status);
  647. if(status == GL_FALSE) error("unable to compile frag shader");
  648. vert_id = glCreateShader(GL_VERTEX_SHADER);
  649. glShaderSource(vert_id, 1, (const GLchar**)&vert, NULL);
  650. glCompileShader(vert_id);
  651. status = 0; glGetShaderiv(vert_id, GL_COMPILE_STATUS, &status);
  652. if(status == GL_FALSE) error("unable to compile vert shader");
  653. prog_id = glCreateProgram();
  654. glAttachShader(prog_id, vert_id);
  655. glAttachShader(prog_id, frag_id);
  656. glLinkProgram(prog_id);
  657. glGetProgramiv(prog_id, GL_LINK_STATUS, &status);
  658. glDetachShader(prog_id, vert_id);
  659. glDetachShader(prog_id, frag_id);
  660. glDeleteShader(vert_id);
  661. glDeleteShader(frag_id);
  662. if(status == GL_FALSE) error("unable to set up shaders");
  663. glUseProgram(prog_id);
  664. proj_id = glGetUniformLocation(prog_id, "proj");
  665. view_id = glGetUniformLocation(prog_id, "view");
  666. tex_id = glGetUniformLocation(prog_id, "tex");
  667. ht_id = glGetUniformLocation(prog_id, "hastex");
  668. amb_id = glGetUniformLocation(prog_id, "ambient");
  669. spe_id = glGetUniformLocation(prog_id, "specular");
  670. shi_id = glGetUniformLocation(prog_id, "shininess");
  671. emi_id = glGetUniformLocation(prog_id, "emission");
  672. model = m3d_load(data, NULL, NULL, NULL);
  673. if(!model) error("unable to parse model");
  674. initmodel();
  675. glGenVertexArrays(1, &vao_id);
  676. glBindVertexArray(vao_id);
  677. glGenBuffers(1, &vbo_id);
  678. glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
  679. glBufferData(GL_ARRAY_BUFFER, numvbo * sizeof(vbo_t), vbo, GL_DYNAMIC_DRAW);
  680. glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vbo_t), 0);
  681. glEnableVertexAttribArray(0);
  682. glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(vbo_t), (void*)(3 * sizeof(float)));
  683. glEnableVertexAttribArray(1);
  684. glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(vbo_t), (void*)(6 * sizeof(float)));
  685. glEnableVertexAttribArray(2);
  686. glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(vbo_t), (void*)(8 * sizeof(float)));
  687. glEnableVertexAttribArray(3);
  688. emscripten_set_main_loop_arg((em_arg_callback_func)main_loop, NULL, -1, 0);
  689. emscripten_set_keydown_callback("body", NULL, 1, key);
  690. emscripten_set_mousemove_callback("canvas", NULL, 1, mouse);
  691. emscripten_set_wheel_callback("canvas", NULL, 1, wheel);
  692. return 0;
  693. }