6 /* -------- sphere -------- */
8 #define SURAD(u) ((u) * 2.0 * M_PI)
9 #define SVRAD(v) ((v) * M_PI)
11 static void sphvec(cgm_vec3 *v, float theta, float phi)
13 v->x = sin(theta) * sin(phi);
15 v->z = cos(theta) * sin(phi);
18 void gen_sphere(struct cmesh *mesh, float rad, int usub, int vsub, float urange, float vrange)
20 int i, j, uverts, vverts, num_verts, num_quads, num_tri, idx;
22 float u, v, du, dv, phi, theta;
23 cgm_vec3 *varr, *narr, *tarr, pos, v0, v1;
26 if(urange == 0.0f || vrange == 0.0f) return;
28 if(usub < 4) usub = 4;
29 if(vsub < 2) vsub = 2;
34 num_verts = uverts * vverts;
35 num_quads = usub * vsub;
36 num_tri = num_quads * 2;
39 varr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_VERTEX, 3, num_verts, 0);
40 narr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_NORMAL, 3, num_verts, 0);
41 tarr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_TANGENT, 3, num_verts, 0);
42 uvarr = (cgm_vec2*)cmesh_set_attrib(mesh, CMESH_ATTR_TEXCOORD2, 2, num_verts, 0);
43 idxarr = (unsigned int*)cmesh_set_index(mesh, num_tri * 3, 0);
45 du = urange / (float)(uverts - 1);
46 dv = vrange / (float)(vverts - 1);
49 for(i=0; i<uverts; i++) {
50 theta = u * 2.0 * M_PI;
53 for(j=0; j<vverts; j++) {
56 sphvec(&pos, theta, phi);
59 cgm_vscale(&pos, rad);
61 sphvec(&v0, theta - 0.1f, (float)M_PI / 2.0f);
62 sphvec(&v1, theta + 0.1f, (float)M_PI / 2.0f);
66 uvarr->x = u / urange;
67 uvarr->y = v / vrange;
70 if(i < usub && j < vsub) {
74 *idxarr++ = idx + vverts + 1;
76 *idxarr++ = idx + vverts;
78 *idxarr++ = idx + vverts + 1;
87 /* ------ geosphere ------ */
90 static cgm_vec3 icosa_pt[] = {
104 enum { P11, P12, P13, P14, P21, P22, P23, P24, P31, P32, P33, P34 };
105 static int icosa_idx[] = {
131 static void geosphere(cgm_vec3 *verts, cgm_vec3 *v1, cgm_vec3 *v2, cgm_vec3 *v3, int iter)
133 cgm_vec3 v12, v23, v31;
136 darr_push(verts, v1);
137 darr_push(verts, v2);
138 darr_push(verts, v3);
144 cgm_vnormalize(&v12);
147 cgm_vnormalize(&v23);
150 cgm_vnormalize(&v31);
152 geosphere(verts, v1, &v12, &v31, iter - 1);
153 geosphere(verts, v2, &v23, &v12, iter - 1);
154 geosphere(verts, v3, &v31, &v23, iter - 1);
155 geosphere(verts, &v12, &v23, &v31, iter - 1);
158 void gen_geosphere(struct cmesh *mesh, float rad, int subdiv, int hemi)
160 int i, j, num_verts, num_tri, vidx;
161 cgm_vec3 v[3], *verts;
162 cgm_vec3 *varr, *narr, *tarr, v0, v1;
166 num_tri = (sizeof icosa_idx / sizeof *icosa_idx) / 3;
168 verts = darr_alloc(0, sizeof *verts);
169 for(i=0; i<num_tri; i++) {
171 vidx = icosa_idx[i * 3 + j];
172 v[j] = icosa_pt[vidx];
173 cgm_vnormalize(v + j);
176 if(hemi && (v[0].y < 0.0 || v[1].y < 0.0 || v[2].y < 0.0)) {
180 geosphere(verts, v, v + 1, v + 2, subdiv);
183 num_verts = darr_size(verts);
186 varr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_VERTEX, 3, num_verts, 0);
187 narr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_NORMAL, 3, num_verts, 0);
188 tarr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_TANGENT, 3, num_verts, 0);
189 uvarr = (cgm_vec2*)cmesh_set_attrib(mesh, CMESH_ATTR_TEXCOORD, 2, num_verts, 0);
191 for(i=0; i<num_verts; i++) {
193 cgm_vscale(varr++, rad);
196 theta = atan2(verts[i].z, verts[i].x);
197 phi = acos(verts[i].y);
199 sphvec(&v0, theta - 0.1f, (float)M_PI / 2.0f);
200 sphvec(&v1, theta + 0.1f, (float)M_PI / 2.0f);
205 uvarr->x = 0.5 * theta / M_PI + 0.5;
206 uvarr->y = phi / M_PI;
211 /* -------- torus ----------- */
212 static void torusvec(cgm_vec3 *v, float theta, float phi, float mr, float rr)
218 rx = -cos(phi) * rr + mr;
222 v->x = rx * sin(theta) + rz * cos(theta);
224 v->z = -rx * cos(theta) + rz * sin(theta);
227 void gen_torus(struct cmesh *mesh, float mainrad, float ringrad, int usub, int vsub, float urange, float vrange)
229 int i, j, uverts, vverts, num_verts, num_quads, num_tri, idx;
230 unsigned int *idxarr;
231 cgm_vec3 *varr, *narr, *tarr, vprev, pos, cent;
233 float u, v, du, dv, theta, phi;
235 if(usub < 4) usub = 4;
236 if(vsub < 2) vsub = 2;
241 num_verts = uverts * vverts;
242 num_quads = usub * vsub;
243 num_tri = num_quads * 2;
246 varr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_VERTEX, 3, num_verts, 0);
247 narr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_NORMAL, 3, num_verts, 0);
248 tarr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_TANGENT, 3, num_verts, 0);
249 uvarr = (cgm_vec2*)cmesh_set_attrib(mesh, CMESH_ATTR_TEXCOORD, 2, num_verts, 0);
250 idxarr = (unsigned int*)cmesh_set_index(mesh, num_tri * 3, 0);
252 du = urange / (float)(uverts - 1);
253 dv = vrange / (float)(vverts - 1);
256 for(i=0; i<uverts; i++) {
257 theta = u * 2.0 * M_PI;
260 for(j=0; j<vverts; j++) {
261 phi = v * 2.0 * M_PI;
263 torusvec(&pos, theta, phi, mainrad, ringrad);
264 torusvec(¢, theta, phi, mainrad, 0.0);
268 cgm_vsub(narr, ¢);
269 cgm_vscale(narr, 1.0f / ringrad);
272 torusvec(&vprev, theta - 0.1f, phi, mainrad, ringrad);
273 torusvec(tarr, theta + 0.1f, phi, mainrad, ringrad);
274 cgm_vsub(tarr, &vprev);
275 cgm_vnormalize(tarr);
278 uvarr->x = u * urange;
279 uvarr->y = v * vrange;
282 if(i < usub && j < vsub) {
283 idx = i * vverts + j;
286 *idxarr++ = idx + vverts + 1;
288 *idxarr++ = idx + vverts;
290 *idxarr++ = idx + vverts + 1;
299 /* -------- cylinder -------- */
301 static void cylvec(cgm_vec3 *v, float theta, float height)
308 void gen_cylinder(struct cmesh *mesh, float rad, float height, int usub, int vsub, int capsub, float urange, float vrange)
310 int i, j, uverts, vverts, num_body_verts, num_body_quads, num_body_tri, idx;
311 int capvverts, num_cap_verts, num_cap_quads, num_cap_tri, num_verts, num_tri;
312 cgm_vec3 *varr, *narr, *tarr, pos, vprev, tang;
314 float y, u, v, du, dv, theta, r;
315 unsigned int *idxarr, vidx[4];
317 if(usub < 4) usub = 4;
318 if(vsub < 1) vsub = 1;
323 num_body_verts = uverts * vverts;
324 num_body_quads = usub * vsub;
325 num_body_tri = num_body_quads * 2;
327 capvverts = capsub ? capsub + 1 : 0;
328 num_cap_verts = uverts * capvverts;
329 num_cap_quads = usub * capsub;
330 num_cap_tri = num_cap_quads * 2;
332 num_verts = num_body_verts + num_cap_verts * 2;
333 num_tri = num_body_tri + num_cap_tri * 2;
336 varr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_VERTEX, 3, num_verts, 0);
337 narr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_NORMAL, 3, num_verts, 0);
338 tarr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_TANGENT, 3, num_verts, 0);
339 uvarr = (cgm_vec2*)cmesh_set_attrib(mesh, CMESH_ATTR_TEXCOORD, 2, num_verts, 0);
340 idxarr = (unsigned int*)cmesh_set_index(mesh, num_tri * 3, 0);
342 du = urange / (float)(uverts - 1);
343 dv = vrange / (float)(vverts - 1);
346 for(i=0; i<uverts; i++) {
350 for(j=0; j<vverts; j++) {
351 y = (v - 0.5) * height;
352 cylvec(&pos, theta, y);
354 cgm_vcons(varr++, pos.x * rad, pos.y, pos.z * rad);
355 cgm_vcons(narr++, pos.x, 0.0f, pos.z);
356 cylvec(&vprev, theta - 0.1f, 0.0f);
357 cylvec(tarr, theta + 0.1f, 0.0f);
358 cgm_vsub(tarr, &vprev);
359 cgm_vnormalize(tarr++);
360 uvarr->x = u * urange;
361 uvarr->y = v * vrange;
364 if(i < usub && j < vsub) {
365 idx = i * vverts + j;
368 *idxarr++ = idx + vverts + 1;
372 *idxarr++ = idx + vverts;
373 *idxarr++ = idx + vverts + 1;
387 dv = 1.0 / (float)(capvverts - 1);
390 for(i=0; i<uverts; i++) {
394 for(j=0; j<capvverts; j++) {
397 cylvec(&pos, theta, height / 2.0f);
399 pos.y = height / 2.0;
400 cylvec(&vprev, theta - 0.1f, 0.0f);
401 cylvec(&tang, theta + 0.1f, 0.0f);
402 cgm_vsub(&tang, &vprev);
403 cgm_vnormalize(&tang);
406 cgm_vcons(narr++, 0, 1, 0);
408 uvarr->x = u * urange;
412 pos.y = -height / 2.0;
414 cgm_vcons(narr++, 0, -1, 0);
415 cgm_vcons(tarr++, -tang.x, -tang.y, -tang.z);
416 uvarr->x = u * urange;
420 if(i < usub && j < capsub) {
421 idx = num_body_verts + (i * capvverts + j) * 2;
424 vidx[1] = idx + capvverts * 2;
425 vidx[2] = idx + (capvverts + 1) * 2;
435 *idxarr++ = vidx[0] + 1;
436 *idxarr++ = vidx[1] + 1;
437 *idxarr++ = vidx[2] + 1;
438 *idxarr++ = vidx[0] + 1;
439 *idxarr++ = vidx[2] + 1;
440 *idxarr++ = vidx[3] + 1;
449 /* -------- cone -------- */
451 static void conevec(cgm_vec3 *v, float theta, float y, float height)
453 float scale = 1.0f - y / height;
454 v->x = sin(theta) * scale;
456 v->z = cos(theta) * scale;
459 void gen_cone(struct cmesh *mesh, float rad, float height, int usub, int vsub, int capsub, float urange, float vrange)
461 int i, j, uverts, vverts, num_body_verts, num_body_quads, num_body_tri, idx;
462 int capvverts, num_cap_verts, num_cap_quads, num_cap_tri, num_verts, num_tri;
463 cgm_vec3 *varr, *narr, *tarr, pos, vprev, tang, bitang;
465 unsigned int *idxarr, vidx[4];
466 float u, v, du, dv, theta, y, r;
468 if(usub < 4) usub = 4;
469 if(vsub < 1) vsub = 1;
474 num_body_verts = uverts * vverts;
475 num_body_quads = usub * vsub;
476 num_body_tri = num_body_quads * 2;
478 capvverts = capsub ? capsub + 1 : 0;
479 num_cap_verts = uverts * capvverts;
480 num_cap_quads = usub * capsub;
481 num_cap_tri = num_cap_quads * 2;
483 num_verts = num_body_verts + num_cap_verts;
484 num_tri = num_body_tri + num_cap_tri;
487 varr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_VERTEX, 3, num_verts, 0);
488 narr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_NORMAL, 3, num_verts, 0);
489 tarr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_TANGENT, 3, num_verts, 0);
490 uvarr = (cgm_vec2*)cmesh_set_attrib(mesh, CMESH_ATTR_TEXCOORD, 2, num_verts, 0);
491 idxarr = (unsigned int*)cmesh_set_index(mesh, num_tri * 3, 0);
493 du = urange / (float)(uverts - 1);
494 dv = vrange / (float)(vverts - 1);
497 for(i=0; i<uverts; i++) {
501 for(j=0; j<vverts; j++) {
503 conevec(&pos, theta, y, height);
505 conevec(&vprev, theta - 0.1f, 0.0f, height);
506 conevec(&tang, theta + 0.1f, 0.0f, height);
507 cgm_vsub(&tang, &vprev);
508 cgm_vnormalize(&tang);
509 conevec(&bitang, theta, y + 0.1f, height);
510 cgm_vsub(&bitang, &pos);
511 cgm_vnormalize(&bitang);
513 cgm_vcons(varr++, pos.x * rad, pos.y, pos.z * rad);
514 cgm_vcross(narr++, &tang, &bitang);
516 uvarr->x = u * urange;
517 uvarr->y = v * vrange;
520 if(i < usub && j < vsub) {
521 idx = i * vverts + j;
524 *idxarr++ = idx + vverts + 1;
528 *idxarr++ = idx + vverts;
529 *idxarr++ = idx + vverts + 1;
538 /* now the bottom cap! */
543 dv = 1.0 / (float)(capvverts - 1);
546 for(i=0; i<uverts; i++) {
550 for(j=0; j<capvverts; j++) {
553 conevec(&pos, theta, 0.0f, height);
555 cylvec(&vprev, theta - 0.1f, 0.0f);
556 cylvec(&tang, theta + 0.1f, 0.0f);
557 cgm_vsub(&tang, &vprev);
558 cgm_vnormalize(&tang);
561 cgm_vcons(narr++, 0, -1, 0);
563 uvarr->x = u * urange;
567 if(i < usub && j < capsub) {
568 idx = num_body_verts + i * capvverts + j;
571 vidx[1] = idx + capvverts;
572 vidx[2] = idx + (capvverts + 1);
590 /* -------- plane -------- */
592 void gen_plane(struct cmesh *mesh, float width, float height, int usub, int vsub)
594 gen_heightmap(mesh, width, height, usub, vsub, 0, 0);
598 /* ----- heightmap ------ */
600 void gen_heightmap(struct cmesh *mesh, float width, float height, int usub, int vsub, float (*hf)(float, float, void*), void *hfdata)
602 int i, j, uverts, vverts, num_verts, num_quads, num_tri, idx;
603 cgm_vec3 *varr, *narr, *tarr, normal, tang, bitan;
605 unsigned int *idxarr;
606 float u, v, du, dv, x, y, z, u1z, v1z;
608 if(usub < 1) usub = 1;
609 if(vsub < 1) vsub = 1;
615 num_verts = uverts * vverts;
617 num_quads = usub * vsub;
618 num_tri = num_quads * 2;
620 varr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_VERTEX, 3, num_verts, 0);
621 narr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_NORMAL, 3, num_verts, 0);
622 tarr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_TANGENT, 3, num_verts, 0);
623 uvarr = (cgm_vec2*)cmesh_set_attrib(mesh, CMESH_ATTR_TEXCOORD, 2, num_verts, 0);
624 idxarr = (unsigned int*)cmesh_set_index(mesh, num_tri * 3, 0);
626 du = 1.0f / (float)usub;
627 dv = 1.0f / (float)vsub;
630 for(i=0; i<uverts; i++) {
632 for(j=0; j<vverts; j++) {
633 x = (u - 0.5) * width;
634 y = (v - 0.5) * height;
635 z = hf ? hf(u, v, hfdata) : 0.0;
637 cgm_vcons(&normal, 0, 0, 1);
639 u1z = hf(u + du, v, hfdata);
640 v1z = hf(u, v + dv, hfdata);
642 cgm_vcons(&tang, du * width, 0, u1z - z);
643 cgm_vcons(&bitan, 0, dv * height, v1z - z);
644 cgm_vcross(&normal, &tang, &bitan);
645 cgm_vnormalize(&normal);
648 cgm_vcons(varr++, x, y, z);
650 cgm_vcons(tarr++, 1, 0, 0);
655 if(i < usub && j < vsub) {
656 idx = i * vverts + j;
659 *idxarr++ = idx + vverts + 1;
663 *idxarr++ = idx + vverts;
664 *idxarr++ = idx + vverts + 1;
673 /* ----- box ------ */
674 void gen_box(struct cmesh *mesh, float xsz, float ysz, float zsz, int usub, int vsub)
676 static const float face_angles[][2] = {
680 {3.0 * M_PI / 2.0, 0},
685 float xform[16], scale[16], idmat[16];
688 if(usub < 1) usub = 1;
689 if(vsub < 1) vsub = 1;
695 gen_plane(m, 1, 1, usub, vsub);
696 cgm_mtranslation(xform, 0, 0, 0.5f);
697 cgm_mrotate_euler(xform, face_angles[i][1], face_angles[i][0], 0.0f, CGM_EULER_XYZ);
698 cmesh_apply_xform(m, xform, 0);
700 cmesh_append(mesh, m);
704 cgm_mscaling(scale, xsz, ysz, zsz);
705 cgm_midentity(idmat);
706 cmesh_apply_xform(mesh, scale, idmat);
710 static inline void rev_vert(cgm_vec3 *res, float u, float v, cgm_vec2 (*rf)(float, float, void*), void *cls)
712 cgm_vec2 pos = rf(u, v, cls);
714 float angle = u * 2.0 * M_PI;
715 res->x = pos.x * cos(angle);
717 res->z = pos.x * sin(angle);
720 /* ------ surface of revolution ------- */
721 void gen_revol(struct cmesh *mesh, int usub, int vsub, cgm_vec2 (*rfunc)(float, float, void*),
722 cgm_vec2 (*nfunc)(float, float, void*), void *cls)
724 int i, j, uverts, vverts, num_verts, num_quads, num_tri, idx;
725 cgm_vec3 *varr, *narr, *tarr, pos, nextu, nextv, tang, normal, bitan;
727 unsigned int *idxarr;
728 float u, v, du, dv, new_v;
731 if(usub < 3) usub = 3;
732 if(vsub < 1) vsub = 1;
738 num_verts = uverts * vverts;
740 num_quads = usub * vsub;
741 num_tri = num_quads * 2;
743 varr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_VERTEX, 3, num_verts, 0);
744 narr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_NORMAL, 3, num_verts, 0);
745 tarr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_TANGENT, 3, num_verts, 0);
746 uvarr = (cgm_vec2*)cmesh_set_attrib(mesh, CMESH_ATTR_TEXCOORD, 2, num_verts, 0);
747 idxarr = (unsigned int*)cmesh_set_index(mesh, num_tri * 3, 0);
749 du = 1.0f / (float)(uverts - 1);
750 dv = 1.0f / (float)(vverts - 1);
753 for(i=0; i<uverts; i++) {
755 for(j=0; j<vverts; j++) {
756 rev_vert(&pos, u, v, rfunc, cls);
758 rev_vert(&nextu, fmod(u + du, 1.0), v, rfunc, cls);
760 cgm_vsub(&tang, &pos);
761 if(cgm_vlength_sq(&tang) < 1e-6) {
762 new_v = v > 0.5f ? v - dv * 0.25f : v + dv * 0.25f;
763 rev_vert(&nextu, fmod(u + du, 1.0f), new_v, rfunc, cls);
765 cgm_vsub(&tang, &pos);
769 rev_vert(&normal, u, v, nfunc, cls);
771 rev_vert(&nextv, u, v + dv, rfunc, cls);
773 cgm_vsub(&bitan, &pos);
774 if(cgm_vlength_sq(&bitan) < 1e-6f) {
775 rev_vert(&nextv, u, v - dv, rfunc, cls);
777 cgm_vsub(&bitan, &nextv);
780 cgm_vcross(&normal, &tang, &bitan);
782 cgm_vnormalize(&normal);
783 cgm_vnormalize(&tang);
792 if(i < usub && j < vsub) {
793 idx = i * vverts + j;
796 *idxarr++ = idx + vverts + 1;
800 *idxarr++ = idx + vverts;
801 *idxarr++ = idx + vverts + 1;
810 static inline void sweep_vert(cgm_vec3 *res, float u, float v, float height,
811 cgm_vec2 (*sf)(float, float, void*), void *cls)
813 cgm_vec2 pos = sf(u, v, cls);
820 /* ---- sweep shape along a path ---- */
821 void gen_sweep(struct cmesh *mesh, float height, int usub, int vsub,
822 cgm_vec2 (*sfunc)(float, float, void*), void *cls)
824 int i, j, uverts, vverts, num_verts, num_quads, num_tri, idx;
825 cgm_vec3 *varr, *narr, *tarr, pos, nextu, nextv, tang, bitan, normal;
827 unsigned int *idxarr;
828 float u, v, du, dv, new_v;
831 if(usub < 3) usub = 3;
832 if(vsub < 1) vsub = 1;
838 num_verts = uverts * vverts;
840 num_quads = usub * vsub;
841 num_tri = num_quads * 2;
843 varr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_VERTEX, 3, num_verts, 0);
844 narr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_NORMAL, 3, num_verts, 0);
845 tarr = (cgm_vec3*)cmesh_set_attrib(mesh, CMESH_ATTR_TANGENT, 3, num_verts, 0);
846 uvarr = (cgm_vec2*)cmesh_set_attrib(mesh, CMESH_ATTR_TEXCOORD, 2, num_verts, 0);
847 idxarr = (unsigned int*)cmesh_set_index(mesh, num_tri * 3, 0);
849 du = 1.0f / (float)(uverts - 1);
850 dv = 1.0f / (float)(vverts - 1);
853 for(i=0; i<uverts; i++) {
855 for(j=0; j<vverts; j++) {
856 sweep_vert(&pos, u, v, height, sfunc, cls);
858 sweep_vert(&nextu, fmod(u + du, 1.0), v, height, sfunc, cls);
860 cgm_vsub(&tang, &pos);
861 if(cgm_vlength_sq(&tang) < 1e-6f) {
862 new_v = v > 0.5f ? v - dv * 0.25f : v + dv * 0.25f;
863 sweep_vert(&nextu, fmod(u + du, 1.0f), new_v, height, sfunc, cls);
865 cgm_vsub(&tang, &pos);
868 sweep_vert(&nextv, u, v + dv, height, sfunc, cls);
870 cgm_vsub(&bitan, &pos);
871 if(cgm_vlength_sq(&bitan) < 1e-6f) {
872 sweep_vert(&nextv, u, v - dv, height, sfunc, cls);
874 cgm_vsub(&bitan, &nextv);
877 cgm_vcross(&normal, &tang, &bitan);
878 cgm_vnormalize(&normal);
879 cgm_vnormalize(&tang);
888 if(i < usub && j < vsub) {
889 idx = i * vverts + j;
892 *idxarr++ = idx + vverts + 1;
896 *idxarr++ = idx + vverts;
897 *idxarr++ = idx + vverts + 1;