1 /*
2 * Copyright 2009-2019, Björn Ståhl
3 * License: 3-Clause BSD, see COPYING file in arcan source repository.
4 * Reference: http://arcan-fe.com
5 */
6
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <math.h>
10 #include <stdint.h>
11 #include <stdbool.h>
12 #include <pthread.h>
13 #include <unistd.h>
14 #include <string.h>
15 #include <stdarg.h>
16 #include <inttypes.h>
17
18 #include <assert.h>
19
20 #include "arcan_math.h"
21 #include "arcan_general.h"
22 #include "arcan_shmif.h"
23 #include "arcan_event.h"
24 #include "arcan_video.h"
25 #include "arcan_videoint.h"
26 #include "arcan_3dbase.h"
27 #include "arcan_vr.h"
28
29 extern struct arcan_video_display arcan_video_display;
30
31 struct camtag_data {
32 _Alignas(16) float projection[16];
33 _Alignas(16) float mvm[16];
34 _Alignas(16) vector wpos;
35 float near;
36 float far;
37 float line_width;
38 enum agp_mesh_flags flags;
39 struct arcan_vr_ctx* vrref;
40 };
41
42 struct geometry {
43 size_t nmaps;
44 agp_shader_id program;
45
46 struct agp_mesh_store store;
47
48 bool complete;
49 bool threaded;
50
51 pthread_t worker;
52 struct geometry* next;
53 };
54
55 typedef struct {
56 pthread_mutex_t lock;
57 int work_count;
58
59 struct geometry* geometry;
60
61 /* AA-BB */
62 vector bbmin;
63 vector bbmax;
64 float radius;
65
66 /* position, opacity etc. are inherited from parent */
67 struct {
68 /* debug geometry (position, normals, bounding box, ...) */
69 bool debug;
70
71 /* unless this flag is set AND there are no pending
72 * load operations, some operations will be deferred. */
73 bool complete;
74
75 /* ignore projection matrix */
76 bool infinite;
77 } flags;
78
79 struct {
80 bool scale;
81 bool swizzle;
82 bool orient;
83 vector orientf;
84 } deferred;
85
86 struct arcan_vr_ctx* vrref;
87
88 arcan_vobject* parent;
89 } arcan_3dmodel;
90
build_plane(point min,point max,point step,float ** verts,unsigned ** indices,float ** txcos,size_t * nverts,size_t * nindices,bool vertical)91 static void build_plane(point min, point max, point step,
92 float** verts, unsigned** indices, float** txcos,
93 size_t* nverts, size_t* nindices, bool vertical)
94 {
95 point delta = {
96 .x = max.x - min.x,
97 .y = max.y,
98 .z = max.z - min.z
99 };
100
101 size_t nx = ceil(delta.x / step.x);
102 size_t nz = ceil(delta.z / step.z);
103
104 *nverts = nx * nz;
105 *verts = arcan_alloc_mem(sizeof(float) * (*nverts) * 3,
106 ARCAN_MEM_MODELDATA, 0, ARCAN_MEMALIGN_PAGE);
107
108 *txcos = arcan_alloc_mem(sizeof(float) * (*nverts) * 2,
109 ARCAN_MEM_MODELDATA, 0, ARCAN_MEMALIGN_PAGE);
110
111 size_t vofs = 0, tofs = 0;
112 for (size_t x = 0; x < nx; x++)
113 for (size_t z = 0; z < nz; z++){
114 (*verts)[vofs++] = min.x + (float)x*step.x;
115 if (vertical){
116 (*verts)[vofs++] = min.z + (float)z*step.z;
117 (*verts)[vofs++] = min.y;
118 }
119 else {
120 (*verts)[vofs++] = min.y;
121 (*verts)[vofs++] = min.z + (float)z*step.z;
122 }
123 (*txcos)[tofs++] = (float)x / (float)(nx-1);
124 (*txcos)[tofs++] = (float)z / (float)(nz-1);
125 }
126
127 vofs = 0;
128 #define GETVERT(X,Z)( ( (X) * nz) + (Z))
129 *indices = arcan_alloc_mem(
130 sizeof(unsigned) * (nx-1) * (nz-1) * 6,
131 ARCAN_MEM_MODELDATA, 0, ARCAN_MEMALIGN_PAGE
132 );
133
134 for (size_t x = 0; x < nx-1; x++)
135 for (size_t z = 0; z < nz-1; z++){
136 (*indices)[vofs++] = GETVERT(x+1, z+1);
137 (*indices)[vofs++] = GETVERT(x, z+1);
138 (*indices)[vofs++] = GETVERT(x, z);
139
140 (*indices)[vofs++] = GETVERT(x+1, z);
141 (*indices)[vofs++] = GETVERT(x+1, z+1);
142 (*indices)[vofs++] = GETVERT(x, z);
143 }
144
145 *nindices = vofs;
146 }
147
freemodel(arcan_3dmodel * src)148 static void freemodel(arcan_3dmodel* src)
149 {
150 if (!src)
151 return;
152
153 if (src->vrref){
154 arcan_vr_release(src->vrref,
155 src->parent ? src->parent->cellid : ARCAN_EID);
156 src->vrref = NULL;
157 }
158
159 struct geometry* geom = src->geometry;
160
161 /* always make sure the model is loaded before freeing */
162 while(geom){
163 if (geom->threaded){
164 pthread_join(geom->worker, NULL);
165 geom->threaded = false;
166 }
167 geom = geom->next;
168 }
169
170 geom = src->geometry;
171
172 /*
173 * special case is where we share buffer between main and sub-geom,
174 * as the agp mesh store has its own concept of a shared buffer, we
175 * also need re-use between stores for various animation/modelling
176 * options.
177 * This means that the base geometry (first slot) can have a store
178 * and subgeometry can alias at offsets for everything except the
179 * shared pointer or vertex pointer
180 **/
181 uintptr_t base;
182 if (geom->store.shared_buffer)
183 base = (uintptr_t) geom->store.shared_buffer;
184 else
185 base = (uintptr_t) geom->store.verts;
186
187 /* save the base geometry slot for last */
188 geom = geom->next;
189 while(geom){
190 uintptr_t base2;
191 if (geom->store.shared_buffer)
192 base2 = (uintptr_t) geom->store.shared_buffer;
193 else
194 base2 = (uintptr_t) geom->store.verts;
195
196 /* subgeom is its own geometry */
197 if (base2 != base)
198 agp_drop_mesh(&geom->store);
199
200 /* delink from list and free */
201 struct geometry* last = geom;
202 geom = geom->next;
203 last->next = NULL;
204 arcan_mem_free(last);
205 }
206
207 agp_drop_mesh(&src->geometry->store);
208 arcan_mem_free(src->geometry);
209 pthread_mutex_destroy(&src->lock);
210 arcan_mem_free(src);
211 }
212
push_deferred(arcan_3dmodel * model)213 static void push_deferred(arcan_3dmodel* model)
214 {
215 if (model->work_count > 0)
216 return;
217
218 if (!model->flags.complete)
219 return;
220
221 if (model->deferred.scale){
222 arcan_3d_scalevertices(model->parent->cellid);
223 model->deferred.scale = false;
224 }
225
226 if (model->deferred.swizzle){
227 arcan_3d_swizzlemodel(model->parent->cellid);
228 model->deferred.swizzle = false;
229 }
230
231 if (model->deferred.orient){
232 arcan_3d_baseorient(model->parent->cellid,
233 model->deferred.orientf.x,
234 model->deferred.orientf.y,
235 model->deferred.orientf.z
236 );
237 model->deferred.orient = false;
238 }
239 }
240
dump_matr(float * matr,const char * pref)241 static void dump_matr(float* matr, const char* pref)
242 {
243 printf("%s = {\n"
244 "%.4f %.4f %.4f %.4f\n"
245 "%.4f %.4f %.4f %.4f\n"
246 "%.4f %.4f %.4f %.4f\n"
247 "%.4f %.4f %.4f %.4f\n"
248 "}\n", pref,
249 matr[0], matr[4], matr[8], matr[12],
250 matr[1], matr[5], matr[9], matr[13],
251 matr[2], matr[6], matr[10], matr[14],
252 matr[3], matr[7], matr[11], matr[15]);
253 }
254
255 /*
256 * Render-loops, Pass control, Initialization
257 */
rendermodel(arcan_vobject * vobj,arcan_3dmodel * src,agp_shader_id baseprog,surface_properties props,float * view,enum agp_mesh_flags flags)258 static void rendermodel(arcan_vobject* vobj, arcan_3dmodel* src,
259 agp_shader_id baseprog, surface_properties props, float* view,
260 enum agp_mesh_flags flags)
261 {
262 assert(vobj);
263
264 if (props.opa < EPSILON || !src->flags.complete || src->work_count > 0)
265 return;
266
267 /* transform order: scale */
268 float _Alignas(16) scale[16] = {
269 props.scale.x, 0.0, 0.0, 0.0,
270 0.0, props.scale.y, 0.0, 0.0,
271 0.0, 0.0, props.scale.z, 0.0,
272 0.0, 0.0, 0.0, 1.0
273 };
274
275 float ox = vobj->origo_ofs.x;
276 float oy = vobj->origo_ofs.y;
277 float oz = vobj->origo_ofs.z;
278
279 /* point-translation to origo_ofs */
280 translate_matrix(scale, ox, oy, oz);
281
282 /* rotate */
283 float _Alignas(16) orient[16];
284 matr_quatf(props.rotation.quaternion, orient);
285 float _Alignas(16) model[16];
286 multiply_matrix(model, orient, scale);
287
288 /* object translation */
289 translate_matrix(model,
290 props.position.x - ox,
291 props.position.y - oy,
292 props.position.z - oz
293 );
294
295 float _Alignas(16) out[16];
296 multiply_matrix(out, view, model);
297
298 agp_shader_envv(MODELVIEW_MATR, out, sizeof(float) * 16);
299 agp_shader_envv(OBJ_OPACITY, &props.opa, sizeof(float));
300
301 struct geometry* base = src->geometry;
302
303 agp_blendstate(vobj->blendmode);
304 int fset_ofs = vobj->frameset ? vobj->frameset->index : 0;
305
306 while (base){
307 agp_shader_activate(base->program > 0 ? base->program : baseprog);
308
309 if (!vobj->frameset)
310 agp_activate_vstore(vobj->vstore);
311 else {
312 if (base->nmaps == 1){
313 agp_activate_vstore(vobj->frameset->frames[fset_ofs].frame);
314 fset_ofs = (fset_ofs + 1) % vobj->frameset->n_frames;
315 }
316 else if (base->nmaps > 1){
317 struct agp_vstore* backing[base->nmaps];
318 for (size_t i = 0; i < base->nmaps; i++){
319 backing[i] = vobj->frameset->frames[fset_ofs].frame;
320 fset_ofs = (fset_ofs + 1) % vobj->frameset->n_frames;
321 }
322 agp_activate_vstore_multi(backing, base->nmaps);
323 }
324 else
325 ;
326 }
327
328 agp_shader_envv(MODELVIEW_MATR, out, sizeof(float) * 16);
329 agp_submit_mesh(&base->store, flags);
330 base = base->next;
331 }
332 }
333
334 enum arcan_ffunc_rv arcan_ffunc_3dobj FFUNC_HEAD
335 {
336 if ( (state.tag == ARCAN_TAG_3DOBJ ||
337 state.tag == ARCAN_TAG_3DCAMERA) && state.ptr){
338
339 switch (cmd){
340 case FFUNC_TICK:
341 break;
342
343 case FFUNC_DESTROY:
344 if (state.tag == ARCAN_TAG_3DOBJ)
345 freemodel( (arcan_3dmodel*) state.ptr );
346 else {
347 arcan_vobject* camobj = arcan_video_getobject(srcid);
348 struct camtag_data* camera = state.ptr;
349 if (camera->vrref){
350 arcan_vr_release(camera->vrref, camobj->cellid);
351 camera->vrref = NULL;
352 }
353 arcan_mem_free(camera);
354 camobj->feed.state.ptr = NULL;
355 }
356 break;
357
358 default:
359 break;
360 }
361 }
362
363 return 0;
364 }
365
366 /* normal scene process, except stops after no objects with infinite
367 * flag (skybox, skygeometry etc.) */
process_scene_infinite(arcan_vobject_litem * cell,float lerp,float * view,enum agp_mesh_flags flags)368 static arcan_vobject_litem* process_scene_infinite(
369 arcan_vobject_litem* cell, float lerp, float* view,
370 enum agp_mesh_flags flags)
371 {
372 arcan_vobject_litem* current = cell;
373 struct rendertarget* rtgt = arcan_vint_current_rt();
374 ssize_t min = 0, max = 65536;
375 if (rtgt){
376 min = rtgt->min_order;
377 max = rtgt->max_order;
378 }
379
380 while (current){
381 arcan_vobject* cvo = current->elem;
382 arcan_3dmodel* obj3d = cvo->feed.state.ptr;
383
384 if (cvo->order >= 0 || obj3d->flags.infinite == false)
385 break;
386
387 ssize_t abs_o = cvo->order * -1;
388 if (abs_o < min){
389 current = current->next;
390 continue;
391 }
392
393 if (abs_o > max)
394 break;
395
396 surface_properties dprops;
397
398 arcan_resolve_vidprop(cvo, lerp, &dprops);
399 rendermodel(cvo,
400 obj3d, cvo->program, dprops, view, flags | MESH_FACING_NODEPTH);
401
402 current = current->next;
403 }
404
405 return current;
406 }
407
process_scene_normal(arcan_vobject_litem * cell,float lerp,float * modelview,enum agp_mesh_flags flags)408 static void process_scene_normal(arcan_vobject_litem* cell,
409 float lerp, float* modelview, enum agp_mesh_flags flags)
410 {
411 arcan_vobject_litem* current = cell;
412 struct rendertarget* rtgt = arcan_vint_current_rt();
413 ssize_t min = 0, max = 65536;
414 if (rtgt){
415 min = rtgt->min_order;
416 max = rtgt->max_order;
417 }
418
419 while (current){
420 arcan_vobject* cvo = current->elem;
421
422 /* non-negative => 2D part of the pipeline, there's nothing
423 * more after this point */
424 if (cvo->order >= 0)
425 break;
426
427 ssize_t abs_o = cvo->order * -1;
428 if (abs_o < min){
429 current = current->next;
430 continue;
431 }
432
433 if (abs_o > max)
434 break;
435
436 surface_properties dprops;
437 arcan_3dmodel* model = cvo->feed.state.ptr;
438 if (model->vrref)
439 dprops = cvo->current;
440 else
441 arcan_resolve_vidprop(cvo, lerp, &dprops);
442 rendermodel(cvo, model, cvo->program, dprops, modelview, flags);
443
444 current = current->next;
445 }
446 }
447
arcan_3d_bindvr(arcan_vobj_id id,struct arcan_vr_ctx * vrref)448 arcan_errc arcan_3d_bindvr(arcan_vobj_id id, struct arcan_vr_ctx* vrref)
449 {
450 arcan_vobject* model = arcan_video_getobject(id);
451 if (!model)
452 return ARCAN_ERRC_NO_SUCH_OBJECT;
453
454 if (model->feed.state.tag == ARCAN_TAG_3DOBJ){
455 ((arcan_3dmodel*)model->feed.state.ptr)->vrref = vrref;
456 return ARCAN_OK;
457 }
458 else if (model->feed.state.tag == ARCAN_TAG_3DCAMERA){
459 struct camtag_data* camera = model->feed.state.ptr;
460 camera->vrref = vrref;
461 return ARCAN_OK;
462 }
463 else
464 return ARCAN_ERRC_UNACCEPTED_STATE;
465 }
466
arcan_3d_viewray(arcan_vobj_id camtag,int x,int y,float fract,vector * pos,vector * ang)467 void arcan_3d_viewray(arcan_vobj_id camtag,
468 int x, int y, float fract, vector* pos, vector* ang)
469 {
470 float z;
471 arcan_vobject* camobj = arcan_video_getobject(camtag);
472 if(!camobj || camobj->feed.state.tag != ARCAN_TAG_3DCAMERA)
473 return;
474 struct camtag_data* camera = camobj->feed.state.ptr;
475 struct monitor_mode mode = platform_video_dimensions();
476
477 dev_coord(&pos->x, &pos->y, &z,
478 x, y, mode.width, mode.height, camera->near, camera->far);
479
480 vector p1 = unproject_matrix(pos->x, pos->y, 0.0,
481 camera->mvm, camera->projection);
482
483 vector p2 = unproject_matrix(pos->x, pos->y, 1.0,
484 camera->mvm, camera->projection);
485
486 p1.z = camera->wpos.z + camera->near;
487 p2.z = camera->wpos.z + camera->far;
488
489 *pos = p1;
490 *ang = norm_vector( sub_vector(p2, p1) );
491 }
492
arcan_3d_obj_bb_intersect(arcan_vobj_id cam,arcan_vobj_id obj,int x,int y)493 bool arcan_3d_obj_bb_intersect(arcan_vobj_id cam,
494 arcan_vobj_id obj, int x, int y)
495 {
496 arcan_vobject* model = arcan_video_getobject(obj);
497 if (!model || model->feed.state.tag != ARCAN_TAG_3DOBJ)
498 return false;
499
500 vector ray_pos;
501 vector ray_dir;
502
503 float rad = ((arcan_3dmodel*)model->feed.state.ptr)->radius;
504 arcan_3d_viewray(cam, x, y, arcan_video_display.c_lerp, &ray_pos, &ray_dir);
505
506 float d1, d2;
507
508 return ray_sphere(&ray_pos, &ray_dir,
509 &model->current.position, rad, &d1, &d2);
510 }
511
512 /* Chained to the video-pass in arcan_video, stop at the
513 * first non-negative order value */
arcan_3d_refresh(arcan_vobj_id camtag,arcan_vobject_litem * cell,float fract)514 arcan_vobject_litem* arcan_3d_refresh(arcan_vobj_id camtag,
515 arcan_vobject_litem* cell, float fract)
516 {
517 arcan_vobject* camobj = arcan_video_getobject(camtag);
518
519 if (!camobj || camobj->feed.state.tag != ARCAN_TAG_3DCAMERA)
520 return cell;
521
522 struct camtag_data* camera = camobj->feed.state.ptr;
523 float _Alignas(16) matr[16];
524 float _Alignas(16) dmatr[16];
525 float _Alignas(16) omatr[16];
526
527 agp_pipeline_hint(PIPELINE_3D);
528 agp_render_options((struct agp_render_options){
529 .line_width = camera->line_width
530 });
531
532 surface_properties dprop;
533 arcan_resolve_vidprop(camobj, fract, &dprop);
534 if (camera->vrref){
535 dprop.rotation = camobj->current.rotation;
536 }
537
538 agp_shader_activate(agp_default_shader(BASIC_3D));
539 agp_shader_envv(PROJECTION_MATR, camera->projection, sizeof(float) * 16);
540
541 /* scale */
542 identity_matrix(matr);
543 scale_matrix(matr, dprop.scale.x, dprop.scale.y, dprop.scale.z);
544
545 /* rotate */
546 matr_quatf(norm_quat(dprop.rotation.quaternion), omatr);
547 multiply_matrix(dmatr, matr, omatr);
548 arcan_3dmodel* obj3d = cell->elem->feed.state.ptr;
549
550 /* "infinite geometry" (skybox) */
551 if (obj3d->flags.infinite)
552 cell = process_scene_infinite(cell, fract, dmatr, camera->flags);
553
554 /* object translate */
555 struct camtag_data* cdata = camobj->feed.state.ptr;
556 cdata->wpos = dprop.position;
557 translate_matrix(dmatr, dprop.position.x, dprop.position.y, dprop.position.z);
558 memcpy(cdata->mvm, dmatr, sizeof(float) * 16);
559
560 process_scene_normal(cell, fract, dmatr, camera->flags);
561
562 return cell;
563 }
564
minmax_verts(vector * minp,vector * maxp,const float * verts,unsigned nverts)565 static void minmax_verts(vector* minp, vector* maxp,
566 const float* verts, unsigned nverts)
567 {
568 for (size_t i = 0; i < nverts * 3; i += 3){
569 vector a = {.x = verts[i], .y = verts[i+1], .z = verts[i+2]};
570 if (a.x < minp->x) minp->x = a.x;
571 if (a.y < minp->y) minp->y = a.y;
572 if (a.z < minp->z) minp->z = a.z;
573 if (a.x > maxp->x) maxp->x = a.x;
574 if (a.y > maxp->y) maxp->y = a.y;
575 if (a.z > maxp->z) maxp->z = a.z;
576 }
577 }
578
579 /* Go through the indices of a model and reverse the winding-
580 * order of its indices or verts so that front/back facing attribute of
581 * each triangle is inverted */
arcan_3d_swizzlemodel(arcan_vobj_id dst)582 arcan_errc arcan_3d_swizzlemodel(arcan_vobj_id dst)
583 {
584 arcan_vobject* vobj = arcan_video_getobject(dst);
585 arcan_errc rv = ARCAN_ERRC_NO_SUCH_OBJECT;
586
587 if (!vobj)
588 return ARCAN_ERRC_NO_SUCH_OBJECT;
589
590 if (vobj->feed.state.tag != ARCAN_TAG_3DOBJ)
591 return ARCAN_ERRC_UNACCEPTED_STATE;
592
593 arcan_3dmodel* model = (arcan_3dmodel*) vobj->feed.state.ptr;
594 pthread_mutex_lock(&model->lock);
595 if (model->work_count != 0 || !model->flags.complete){
596 model->deferred.swizzle = true;
597 pthread_mutex_unlock(&model->lock);
598 return ARCAN_OK;
599 }
600
601 struct geometry* curr = model->geometry;
602 while (curr) {
603 if (curr->store.indices){
604 unsigned* indices = curr->store.indices;
605 for (size_t i = 0; i <curr->store.n_indices; i+= 3){
606 unsigned iv = indices[i];
607 indices[i] = indices[i+2];
608 indices[i+2] = iv;
609 }
610 } else {
611 float* verts = curr->store.verts;
612
613 for (size_t i = 0; i < curr->store.n_vertices * 9; i+= 9){
614 vector v1 = { .x = verts[i ], .y = verts[i+1], .z = verts[i+2] };
615 vector v3 = { .x = verts[i+6], .y = verts[i+7], .z = verts[i+8] };
616 verts[i ] = v3.x; verts[i+1] = v3.y; verts[i+2] = v3.z;
617 verts[i+6] = v1.x; verts[i+7] = v1.y; verts[i+8] = v1.z;
618 }
619 }
620
621 curr = curr->next;
622 }
623
624 pthread_mutex_unlock(&model->lock);
625 return rv;
626 }
627
arcan_3d_pointcloud(size_t count,size_t nmaps)628 arcan_vobj_id arcan_3d_pointcloud(size_t count, size_t nmaps)
629 {
630 if (count == 0)
631 return ARCAN_EID;
632
633 vfunc_state state = {.tag = ARCAN_TAG_3DOBJ};
634 img_cons empty = {0};
635 arcan_vobj_id rv = arcan_video_addfobject(FFUNC_3DOBJ, state, empty, 1);
636
637 if (rv == ARCAN_EID)
638 return ARCAN_EID;
639
640 arcan_3dmodel* newmodel = arcan_alloc_mem(sizeof(arcan_3dmodel),
641 ARCAN_MEM_VTAG, ARCAN_MEM_BZERO, ARCAN_MEMALIGN_NATURAL);
642 state.ptr = newmodel;
643 arcan_video_alterfeed(rv, FFUNC_3DOBJ, state);
644 pthread_mutex_init(&newmodel->lock, NULL);
645
646 newmodel->geometry = arcan_alloc_mem(sizeof(struct geometry), ARCAN_MEM_VTAG,
647 ARCAN_MEM_BZERO, ARCAN_MEMALIGN_NATURAL);
648 newmodel->geometry->store.n_vertices = count;
649 newmodel->geometry->store.vertex_size = 3;
650 newmodel->geometry->nmaps = nmaps;
651 newmodel->geometry->store.verts = arcan_alloc_mem(sizeof(float) * count * 3,
652 ARCAN_MEM_MODELDATA, 0, ARCAN_MEMALIGN_PAGE);
653 newmodel->geometry->store.txcos = arcan_alloc_mem(sizeof(float) * count * 2,
654 ARCAN_MEM_MODELDATA, 0, ARCAN_MEMALIGN_PAGE);
655
656 float step = 2.0f / sqrtf(count);
657
658 float cz = -1;
659 float cx = -1;
660 float* dbuf = newmodel->geometry->store.verts;
661 float* tbuf = newmodel->geometry->store.txcos;
662
663 /* evenly distribute texture coordinates, randomly distribute vertices */
664 while(count--){
665 cx = cx + step;
666 if (cx > 1){
667 cx = -1;
668 cz = cz + step;
669 }
670
671 float x = 1.0 - (float)rand()/(float)(RAND_MAX/2.0);
672 float y = 1.0 - (float)rand()/(float)(RAND_MAX/2.0);
673 float z = 1.0 - (float)rand()/(float)(RAND_MAX/2.0);
674
675 *dbuf++ = x;
676 *dbuf++ = y;
677 *dbuf++ = z;
678 *tbuf++ = (cx + 1.0) / 2.0;
679 *tbuf++ = (cz + 1.0) / 2.0;
680 }
681
682 newmodel->radius = 1.0;
683
684 vector bbmin = {-1, -1, -1};
685 vector bbmax = { 1, 1, 1};
686 newmodel->bbmin = bbmin;
687 newmodel->bbmax = bbmax;
688 newmodel->flags.complete = true;
689 newmodel->flags.debug = true;
690 newmodel->geometry->nmaps = nmaps;
691 newmodel->geometry->store.type = AGP_MESH_POINTCLOUD;
692
693 return rv;
694 }
695
arcan_3d_buildcylinder(float r,float hh,size_t steps,size_t nmaps,int fill_mode)696 arcan_vobj_id arcan_3d_buildcylinder(float r,
697 float hh, size_t steps, size_t nmaps, int fill_mode)
698 {
699 vfunc_state state = {.tag = ARCAN_TAG_3DOBJ};
700 img_cons empty = {0};
701
702 if (hh < EPSILON || steps < 1)
703 return ARCAN_EID;
704
705 arcan_vobj_id rv = arcan_video_addfobject(FFUNC_3DOBJ, state, empty, 1);
706 if (rv == ARCAN_EID)
707 return rv;
708
709 /* control structure */
710 arcan_3dmodel* newmodel = arcan_alloc_mem(sizeof(arcan_3dmodel),
711 ARCAN_MEM_VTAG, ARCAN_MEM_BZERO, ARCAN_MEMALIGN_NATURAL);
712 state.ptr = (void*) newmodel;
713 arcan_video_alterfeed(rv, FFUNC_3DOBJ, state);
714 pthread_mutex_init(&newmodel->lock, NULL);
715 arcan_video_allocframes(rv, 1, ARCAN_FRAMESET_SPLIT);
716
717 /* metadata / geometry source */
718 newmodel->geometry = arcan_alloc_mem(
719 sizeof(struct geometry), ARCAN_MEM_VTAG, ARCAN_MEM_BZERO, ARCAN_MEMALIGN_NATURAL);
720
721 /* total number of verts, normals, textures and indices */
722 int caps = fill_mode > CYLINDER_FILL_HALF;
723 size_t n_verts = 3 * (steps * 3 + caps * steps * 2);
724 size_t n_txcos = 3 * (steps * 2 + caps * steps * 2);
725 size_t n_normals = 3 * (steps * 3 + caps * steps * 2);
726 size_t n_indices = 1 * (steps * 6 + caps * steps * 6);
727 size_t buf_sz =
728 sizeof(float) * (n_verts + n_normals + n_txcos) + n_indices * sizeof(unsigned);
729
730 /* build our storage buffers */
731 float* dbuf = arcan_alloc_mem(buf_sz, ARCAN_MEM_MODELDATA, 0, ARCAN_MEMALIGN_SIMD);
732 float* vp = dbuf;
733 float* np = &dbuf[n_verts];
734 float* tp = &dbuf[n_txcos + n_verts];
735 float* nn = &dbuf[n_normals + n_txcos + n_verts];
736 unsigned* ip = (unsigned*) (&dbuf[n_verts + n_txcos + n_normals]);
737
738 /* map it all into our model */
739 newmodel->geometry->store.vertex_size = 3;
740 newmodel->geometry->store.type = AGP_MESH_TRISOUP;
741 newmodel->geometry->store.shared_buffer_sz = buf_sz;
742 newmodel->geometry->store.shared_buffer = (uint8_t*) dbuf;
743 newmodel->geometry->store.verts = vp;
744 newmodel->geometry->store.txcos = tp;
745 newmodel->geometry->store.normals = np;
746 newmodel->geometry->store.indices = ip;
747 newmodel->geometry->store.n_indices = n_indices;
748 newmodel->geometry->store.n_vertices = n_verts / 3;
749 newmodel->geometry->nmaps = nmaps;
750 newmodel->geometry->complete = true;
751 newmodel->radius = r > (2.0 * hh) ? r : 2.0 * hh;
752 newmodel->bbmin = (vector){.x = -r, .y = -hh, .z = -r};
753 newmodel->bbmax = (vector){.x = r, .y = hh, .z = r};
754 newmodel->flags.complete = true;
755
756 /* pass one, base data */
757 size_t vc = 0;
758 float step_sz = 2 * M_PI / (float) steps;
759 float txf = 2 * M_PI;
760
761 if (fill_mode == CYLINDER_FILL_HALF || fill_mode == CYLINDER_FILL_HALF_CAPS){
762 step_sz = M_PI / (float) steps;
763 txf = M_PI;
764 }
765
766 for (size_t i = 0; i <= steps; i++){
767 float p = (float) i * step_sz;
768 float x = cosf(p);
769 float z = sinf(p);
770
771 /* top */
772 *vp++ = r * x; *vp++ = hh; *vp++ = r * z;
773 *tp++ = p / txf; *tp++ = 0;
774 *np++ = x; *np++ = 0; *np++ = z;
775
776 /* bottom */
777 *vp++ = r * x; *vp++ = -hh; *vp++ = r * z;
778 *tp++ = p / txf; *tp++ = 1;
779 *np++ = x; *np++ = 0; *np++ = z;
780 vc += 2;
781 }
782
783 /* pass two, index buffer */
784 size_t ic = 0;
785 int ofs = 0;
786
787 if (fill_mode == CYLINDER_FILL_HALF || fill_mode == CYLINDER_FILL_HALF_CAPS)
788 ofs = -1;
789
790 for (size_t i = 0; i < steps + ofs; i++){
791 unsigned i1 = (i * 2 + 0);
792 unsigned i2 = (i * 2 + 1);
793 unsigned i3 = (i * 2 + 2);
794 unsigned i4 = (i * 2 + 3);
795 ic += 6;
796 *ip++ = i2;
797 *ip++ = i3;
798 *ip++ = i1;
799 *ip++ = i4;
800 *ip++ = i3;
801 *ip++ = i2;
802 }
803
804 /*
805 *ip++ = (steps-1)*2+2;
806 *ip++ = (steps-1)*2+1;
807 *ip++ = (steps-1)*2+0;
808 *ip++ = 2;
809 *ip++ = 3;
810 *ip++ = 1;
811 ic += 6;
812 */
813
814 newmodel->geometry->store.n_indices = ic;
815
816 /* pass three, endcaps - only weird bit is that we don't really texture */
817 if (caps){
818 *vp++ = 0;
819 *vp++ = hh;
820 *vp++ = 0;
821 *tp++ = 0;
822 *tp++ = 0;
823 *np++ = 0;
824 *np++ = 1;
825 *np++ = 0;
826 *vp++ = 0;
827 *vp++ = -hh;
828 *vp++ = 0;
829 *tp++ = 0;
830 *tp++ = 0;
831 *np++ = 0;
832 *np++ = 1;
833 *np++ = 0;
834 }
835
836 return rv;
837 }
838
arcan_3d_buildsphere(float r,unsigned l,unsigned m,bool hemi,size_t nmaps)839 arcan_vobj_id arcan_3d_buildsphere(
840 float r, unsigned l, unsigned m, bool hemi, size_t nmaps)
841 {
842 vfunc_state state = {.tag = ARCAN_TAG_3DOBJ};
843 img_cons empty = {0};
844
845 if (l <= 1 || m <= 1)
846 return ARCAN_EID;
847
848 arcan_vobj_id rv = arcan_video_addfobject(FFUNC_3DOBJ, state, empty, 1);
849 if (rv == ARCAN_EID)
850 return rv;
851
852 arcan_3dmodel* newmodel = arcan_alloc_mem(sizeof(arcan_3dmodel),
853 ARCAN_MEM_VTAG, ARCAN_MEM_BZERO, ARCAN_MEMALIGN_NATURAL);
854 state.ptr = (void*) newmodel;
855 arcan_video_alterfeed(rv, FFUNC_3DOBJ, state);
856 pthread_mutex_init(&newmodel->lock, NULL);
857 arcan_video_allocframes(rv, 1, ARCAN_FRAMESET_SPLIT);
858
859 /* total number of verts, normals, textures and indices */
860 size_t nv = l * m * 3;
861 size_t nn = l * m * 3;
862 size_t nt = l * m * 2;
863 size_t ni = (l-1) * (m-1) * 6;
864 size_t buf_sz = (nv + nn + nt) * sizeof(float) + ni * sizeof(unsigned);
865
866 /* build our storage buffers */
867 float* dbuf = arcan_alloc_mem(buf_sz, ARCAN_MEM_MODELDATA, 0, ARCAN_MEMALIGN_SIMD);
868 float* vp = dbuf;
869 float* np = &dbuf[nv];
870 float* tp = &dbuf[nv + nn];
871 unsigned* ip = (unsigned*) (&dbuf[nv + nn + nt]);
872
873 /* map it all into our model */
874 newmodel->geometry = arcan_alloc_mem(
875 sizeof(struct geometry), ARCAN_MEM_VTAG, ARCAN_MEM_BZERO, ARCAN_MEMALIGN_NATURAL);
876 newmodel->geometry->store.vertex_size = 3;
877 newmodel->geometry->store.type = AGP_MESH_TRISOUP;
878 newmodel->geometry->store.shared_buffer_sz = buf_sz;
879 newmodel->geometry->store.shared_buffer = (uint8_t*) dbuf;
880 newmodel->geometry->store.verts = vp;
881 newmodel->geometry->store.txcos = tp;
882 newmodel->geometry->store.normals = np;
883 newmodel->geometry->store.indices = ip;
884 newmodel->geometry->store.n_indices = ni;
885 newmodel->geometry->store.n_vertices = nv / 3;
886 newmodel->geometry->complete = true;
887 newmodel->radius = r;
888 newmodel->flags.complete = true;
889 /* bbmin, bbmax */
890
891 /* pass one, base data */
892 float step_l = 1.0f / (float)(l - 1);
893 float step_m = 1.0f / (float)(m - 1);
894 float hcons;
895 float yofs;
896 if (hemi){
897 hcons = 0;
898 yofs = -0.5*r;
899 }
900 else{
901 hcons = -M_PI_2;
902 yofs = 0.0;
903 }
904
905 for (int L = 0; L < l; L++){
906 for (int M = 0; M < m; M++){
907 float y = sinf( hcons + M_PI * L * step_l ) + yofs;
908 float x = cosf(2.0f*M_PI*(float)M*step_m) * sinf(M_PI*(float)L*step_l);
909 float z = sinf(2.0f*M_PI*(float)M*step_m) * sinf(M_PI*(float)L*step_l);
910 *tp++ = (float)M * step_m;
911 *tp++ = 1.0 - (float)L * step_l;
912 *vp++ = x * r;
913 *vp++ = y * r;
914 *vp++ = z * r;
915 *np++ = x;
916 *np++ = y;
917 *np++ = z;
918 }
919 }
920
921 /* pass two, indexing primitives, take out 4 points on the quad and split */
922 for (int L = 0; L < l - 1; L++){
923 for (int M = 0; M < m - 1; M++){
924 unsigned i1 = L * m + M;
925 unsigned i2 = L * m + M + 1;
926 unsigned i3 = (L+1) * m + M + 1;
927 unsigned i4 = (L+1) * m + M;
928 *ip++ = i1; *ip++ = i2; *ip++ = i3;
929 *ip++ = i1; *ip++ = i3; *ip++ = i4;
930 }
931 }
932
933 return rv;
934 }
935
arcan_3d_buildbox(float w,float h,float d,size_t nmaps,bool s)936 arcan_vobj_id arcan_3d_buildbox(float w, float h, float d, size_t nmaps, bool s)
937 {
938 vfunc_state state = {.tag = ARCAN_TAG_3DOBJ};
939 img_cons empty = {0};
940 arcan_vobj_id rv = arcan_video_addfobject(FFUNC_3DOBJ, state, empty, 1);
941
942 if (rv == ARCAN_EID)
943 return rv;
944
945 /* wait with setting the full model until we know we have a fobject */
946 arcan_3dmodel* newmodel = arcan_alloc_mem(sizeof(arcan_3dmodel),
947 ARCAN_MEM_VTAG, ARCAN_MEM_BZERO, ARCAN_MEMALIGN_NATURAL);
948 state.ptr = (void*) newmodel;
949 arcan_video_alterfeed(rv, FFUNC_3DOBJ, state);
950 pthread_mutex_init(&newmodel->lock, NULL);
951 arcan_video_allocframes(rv, 1, ARCAN_FRAMESET_SPLIT);
952
953 /* winding order: clockwise */
954 float verts[] = {
955 -w, h,-d, /* TOP */
956 -w, h, d,
957 w, h, d,
958 w, h,-d,
959 -w, h, d, /* LEFT */
960 -w,-h, d,
961 -w,-h,-d,
962 -w, h,-d,
963 w, h, d, /* RIGHT */
964 w,-h, d,
965 w,-h,-d,
966 w, h,-d,
967 w, h, d, /* FRONT */
968 w,-h, d,
969 -w,-h, d,
970 -w, h, d,
971 w, h,-d, /* BACK */
972 w,-h,-d,
973 -w,-h,-d,
974 -w, h,-d,
975 -w,-h,-d, /* BOTTOM */
976 -w,-h, d,
977 w,-h, d,
978 w,-h,-d
979 };
980
981 float txcos[] = {
982 0, 1, /* TOP */
983 0, 0,
984 1, 0,
985 1, 1,
986 0, 0, /* LEFT */
987 0, 1,
988 1, 1,
989 1, 0,
990 1, 0, /* RIGHT */
991 1, 1,
992 0, 1,
993 0, 0,
994 0, 0, /* FRONT */
995 0, 1,
996 1, 1,
997 1, 0,
998 1, 0, /* BACK */
999 1, 1,
1000 0, 1,
1001 0, 0,
1002 0, 0, /* BOTTOM */
1003 0, 1,
1004 1, 1,
1005 1, 0
1006 };
1007
1008 float normals[] = {
1009 0, 1, 0, /* TOP */
1010 0, 1, 0,
1011 0, 1, 0,
1012 0, 1, 0,
1013 -1, 0, 0, /* LEFT */
1014 -1, 0, 0,
1015 -1, 0, 0,
1016 -1, 0, 0,
1017 1, 0, 0, /* RIGHT */
1018 1, 0, 0,
1019 1, 0, 0,
1020 1, 0, 0,
1021 0, 0, -1, /* FRONT */
1022 0, 0, -1,
1023 0, 0, -1,
1024 0, 0, -1,
1025 0, 0, 1, /* FRONT */
1026 0, 0, 1,
1027 0, 0, 1,
1028 0, 0, 1,
1029 0, -1, 0, /* BOTTOM */
1030 0, -1, 0,
1031 0, -1, 0,
1032 0, -1, 0
1033 };
1034
1035 unsigned indices[] = {
1036 10, 9, 8, /* right */
1037 11, 10, 8,
1038 6, 4, 5, /* left */
1039 7, 4, 6,
1040 2, 1, 0, /* top */
1041 3, 2, 0,
1042 22, 20, 21, /* bottom */
1043 23, 20, 22,
1044 18, 17, 16, /* back */
1045 19, 18, 16,
1046 14, 12, 13, /* front */
1047 12, 14, 15,
1048 };
1049
1050 vector bbmin = {.x = -w, .y = -h, .z = -d};
1051 vector bbmax = {.x = w, .y = h, .z = d};
1052
1053 /* one big allocation for everything */
1054 size_t buf_sz =
1055 sizeof(verts) + sizeof(txcos) + sizeof(indices) + sizeof(normals);
1056
1057 float* dbuf = arcan_alloc_mem(buf_sz, ARCAN_MEM_MODELDATA, 0, ARCAN_MEMALIGN_SIMD);
1058 size_t nofs = COUNT_OF(verts);
1059 size_t tofs = nofs + COUNT_OF(normals);
1060 size_t iofs = tofs + COUNT_OF(txcos);
1061
1062 memcpy(dbuf, verts, sizeof(verts));
1063 memcpy(&dbuf[nofs], normals, sizeof(normals));
1064 memcpy(&dbuf[tofs], txcos, sizeof(txcos));
1065 memcpy(&dbuf[iofs], indices, sizeof(indices));
1066
1067 if (s){
1068 struct geometry** geom = &newmodel->geometry;
1069 struct geometry* last = NULL;
1070
1071 unsigned* indices = (unsigned*) &dbuf[iofs];
1072
1073 for (size_t i = 0; i < 6; i++){
1074 *geom = arcan_alloc_mem(
1075 sizeof(struct geometry), ARCAN_MEM_MODELDATA, ARCAN_MEM_BZERO, 0);
1076 (*geom)->store.shared_buffer = (uint8_t*) dbuf;
1077 (*geom)->store.shared_buffer_sz = buf_sz;
1078 (*geom)->store.verts = dbuf;
1079 (*geom)->store.txcos = &dbuf[tofs];
1080 (*geom)->store.normals = &dbuf[nofs];
1081 (*geom)->store.indices = &indices[i*6];
1082 (*geom)->store.n_indices = 6;
1083 (*geom)->store.vertex_size = 3;
1084 (*geom)->store.n_vertices = COUNT_OF(verts);
1085 (*geom)->nmaps = nmaps;
1086 (*geom)->complete = true;
1087 geom = &(*geom)->next;
1088 }
1089 }
1090 else{
1091 newmodel->geometry = arcan_alloc_mem(
1092 sizeof(struct geometry), ARCAN_MEM_VTAG, ARCAN_MEM_BZERO, ARCAN_MEMALIGN_NATURAL);
1093 newmodel->geometry->nmaps = nmaps;
1094 newmodel->geometry->store.vertex_size = 3;
1095 newmodel->geometry->store.type = AGP_MESH_TRISOUP;
1096 newmodel->geometry->store.shared_buffer_sz = buf_sz;
1097 newmodel->geometry->store.verts = dbuf;
1098 newmodel->geometry->store.txcos = &dbuf[tofs];
1099 newmodel->geometry->store.normals = &dbuf[nofs];
1100 newmodel->geometry->store.indices = (unsigned*) &dbuf[iofs];
1101 newmodel->geometry->store.n_indices = COUNT_OF(indices);
1102 newmodel->geometry->store.n_vertices = COUNT_OF(verts);
1103 newmodel->geometry->store.shared_buffer = (uint8_t*) dbuf;
1104 newmodel->geometry->complete = true;
1105 }
1106
1107 newmodel->radius = d;
1108 newmodel->bbmin = bbmin;
1109 newmodel->bbmax = bbmax;
1110 newmodel->flags.complete = true;
1111
1112 return rv;
1113 }
1114
arcan_3d_buildplane(float mins,float mint,float maxs,float maxt,float base,float wdens,float ddens,size_t nmaps,bool vert)1115 arcan_vobj_id arcan_3d_buildplane(
1116 float mins, float mint, float maxs, float maxt,
1117 float base, float wdens, float ddens, size_t nmaps, bool vert)
1118 {
1119 vfunc_state state = {.tag = ARCAN_TAG_3DOBJ};
1120 arcan_vobj_id rv = ARCAN_EID;
1121 img_cons empty = {0};
1122
1123 /* fail on unsolvable dimension constraints */
1124 if ( (maxs < mins || wdens <= 0 || wdens >= maxs - mins) ||
1125 (maxt < mint || ddens <= 0 || ddens >= maxt - mint) )
1126 return ARCAN_ERRC_BAD_ARGUMENT;
1127
1128 rv = arcan_video_addfobject(FFUNC_3DOBJ, state, empty, 1);
1129 arcan_3dmodel* newmodel = NULL;
1130
1131 if (rv == ARCAN_EID)
1132 return rv;
1133
1134 /* if [vert] we flip y and z axis when setting vertices */
1135 point minp = {.x = mins, .y = base, .z = mint};
1136 point maxp = {.x = maxs, .y = base, .z = maxt};
1137 point step = {.x = wdens, .y = 0, .z = ddens};
1138
1139 newmodel = arcan_alloc_mem(sizeof(arcan_3dmodel),
1140 ARCAN_MEM_VTAG, ARCAN_MEM_BZERO, ARCAN_MEMALIGN_PAGE);
1141
1142 pthread_mutex_init(&newmodel->lock, NULL);
1143
1144 state.ptr = (void*) newmodel;
1145 arcan_video_alterfeed(rv, FFUNC_3DOBJ, state);
1146
1147 struct geometry** nextslot = &(newmodel->geometry);
1148 while (*nextslot)
1149 nextslot = &((*nextslot)->next);
1150
1151 *nextslot = arcan_alloc_mem(sizeof(struct geometry),
1152 ARCAN_MEM_VTAG, ARCAN_MEM_BZERO, ARCAN_MEMALIGN_PAGE);
1153
1154 (*nextslot)->nmaps = nmaps;
1155 newmodel->geometry = *nextslot;
1156 newmodel->geometry->store.type = AGP_MESH_TRISOUP;
1157 newmodel->geometry->store.vertex_size = 3;
1158
1159 struct geometry* dst = newmodel->geometry;
1160
1161 build_plane(minp, maxp, step, &dst->store.verts, &dst->store.indices,
1162 &dst->store.txcos, &dst->store.n_vertices, &dst->store.n_indices, vert);
1163
1164 arcan_video_allocframes(rv, 1, ARCAN_FRAMESET_SPLIT);
1165
1166 /* though we do know the bounding box and shouldn't need to calculate
1167 * or iterate, plan is to possibly add transform / lookup functions
1168 * during creation step, so this is a precaution */
1169 minmax_verts(&newmodel->bbmin, &newmodel->bbmax,
1170 dst->store.verts, dst->store.n_vertices);
1171 dst->complete = true;
1172 newmodel->flags.complete = true;
1173
1174 return rv;
1175 }
1176
1177 /*
1178 static void invert_txcos(float* buf, unsigned bufs){
1179 for (unsigned i = 0; i < bufs; i+= 2){
1180 float a = buf[i+1];
1181 buf[i+1] = buf[i];
1182 buf[i] = a;
1183 }
1184 }
1185 */
1186
arcan_3d_meshshader(arcan_vobj_id dst,agp_shader_id shid,unsigned slot)1187 arcan_errc arcan_3d_meshshader(arcan_vobj_id dst,
1188 agp_shader_id shid, unsigned slot)
1189 {
1190 arcan_vobject* vobj = arcan_video_getobject(dst);
1191
1192 if (!vobj)
1193 return ARCAN_ERRC_NO_SUCH_OBJECT;
1194
1195 if (vobj->feed.state.tag != ARCAN_TAG_3DOBJ)
1196 return ARCAN_ERRC_UNACCEPTED_STATE;
1197
1198 struct geometry* cur = ((arcan_3dmodel*)vobj->feed.state.ptr)->geometry;
1199 while (cur && slot){
1200 cur = cur->next;
1201 slot--;
1202 }
1203
1204 if (cur && slot <= 0)
1205 cur->program = shid;
1206 else
1207 return ARCAN_ERRC_BAD_ARGUMENT;
1208
1209 return ARCAN_OK;
1210 }
1211
1212 struct threadarg{
1213 arcan_3dmodel* model;
1214 struct geometry* geom;
1215 data_source resource;
1216 map_region datamap;
1217 off_t readofs;
1218 };
1219
arcan_3d_addraw(arcan_vobj_id dst,float * vertices,size_t n_vertices,unsigned * indices,size_t n_indices,float * txcos,float * txcos2,float * normals,float * tangents,float * colors,uint16_t bones[4],float weights[4],unsigned nmaps)1220 arcan_errc arcan_3d_addraw(arcan_vobj_id dst,
1221 float* vertices, size_t n_vertices,
1222 unsigned* indices, size_t n_indices,
1223 float* txcos, float* txcos2,
1224 float* normals, float* tangents,
1225 float* colors,
1226 uint16_t bones[4], float weights[4],
1227 unsigned nmaps)
1228 {
1229 arcan_vobject* vobj = arcan_video_getobject(dst);
1230
1231 if (!vobj)
1232 return ARCAN_ERRC_NO_SUCH_OBJECT;
1233
1234 arcan_3dmodel* model = vobj->feed.state.ptr;
1235
1236 if (vobj->feed.state.tag != ARCAN_TAG_3DOBJ ||
1237 model->flags.complete == true)
1238 return ARCAN_ERRC_UNACCEPTED_STATE;
1239
1240 /* find last elem and add */
1241 struct geometry** nextslot = &(model->geometry);
1242 while (*nextslot)
1243 nextslot = &((*nextslot)->next);
1244
1245 *nextslot = arcan_alloc_mem(sizeof(struct geometry),
1246 ARCAN_MEM_VTAG, ARCAN_MEM_BZERO, ARCAN_MEMALIGN_NATURAL);
1247 struct geometry* dg = *nextslot;
1248
1249 if (!dg)
1250 return ARCAN_ERRC_OUT_OF_SPACE;
1251
1252 dg->nmaps = nmaps;
1253 dg->store.type = AGP_MESH_TRISOUP;
1254 dg->store.verts = vertices;
1255 dg->store.indices = indices;
1256 dg->store.normals = normals;
1257 dg->store.tangents = tangents;
1258 /* FIXME: spawn thread to calculate tangents if missing, and calculate
1259 * bitangents after that - cprod(normal, tangent.wyz) * tangent.w
1260 */
1261 dg->store.txcos = txcos;
1262 dg->store.txcos2 = txcos2;
1263 dg->store.colors = colors;
1264 dg->store.joints = bones;
1265 dg->store.weights = weights;
1266 dg->store.n_vertices = n_vertices;
1267 dg->store.vertex_size = 3;
1268 dg->store.n_indices = n_indices;
1269
1270 return ARCAN_OK;
1271 }
1272
arcan_3d_addmesh(arcan_vobj_id dst,data_source resource,unsigned nmaps)1273 arcan_errc arcan_3d_addmesh(arcan_vobj_id dst,
1274 data_source resource, unsigned nmaps)
1275 {
1276 arcan_vobject* vobj = arcan_video_getobject(dst);
1277
1278 if (!vobj)
1279 return ARCAN_ERRC_NO_SUCH_OBJECT;
1280
1281 arcan_3dmodel* model = vobj->feed.state.ptr;
1282
1283 if (1 ||
1284 vobj->feed.state.tag != ARCAN_TAG_3DOBJ ||
1285 model->flags.complete == true)
1286 return ARCAN_ERRC_UNACCEPTED_STATE;
1287
1288 /*
1289 * commented out for now until we can add the model parsing to fsrv_decode
1290 *
1291 struct threadarg* arg = arcan_alloc_mem(sizeof(struct threadarg),
1292 ARCAN_MEM_THREADCTX, 0, ARCAN_MEMALIGN_NATURAL);
1293
1294 arg->model = model;
1295 arg->resource = resource;
1296 arg->readofs = 0;
1297 arg->datamap = arcan_map_resource(&arg->resource, false);
1298 if (arg->datamap.ptr == NULL){
1299 arcan_mem_free(arg);
1300 return ARCAN_ERRC_BAD_RESOURCE;
1301 }
1302
1303 struct geometry** nextslot = &(model->geometry);
1304 while (*nextslot)
1305 nextslot = &((*nextslot)->next);
1306
1307 *nextslot = arcan_alloc_mem(sizeof(struct geometry), ARCAN_MEM_VTAG,
1308 ARCAN_MEM_BZERO, ARCAN_MEMALIGN_NATURAL);
1309
1310 (*nextslot)->nmaps = nmaps;
1311 (*nextslot)->store.type = AGP_MESH_TRISOUP;
1312
1313 arg->geom = *nextslot;
1314
1315 pthread_mutex_lock(&model->lock);
1316 model->work_count++;
1317 pthread_mutex_unlock(&model->lock);
1318
1319 arg->geom->threaded = true;
1320 */
1321
1322 return ARCAN_OK;
1323 }
1324
arcan_3d_scalevertices(arcan_vobj_id vid)1325 arcan_errc arcan_3d_scalevertices(arcan_vobj_id vid)
1326 {
1327 arcan_vobject* vobj = arcan_video_getobject(vid);
1328
1329 if (!vobj)
1330 return ARCAN_ERRC_NO_SUCH_OBJECT;
1331
1332 if (vobj->feed.state.tag != ARCAN_TAG_3DOBJ){
1333 return ARCAN_ERRC_UNACCEPTED_STATE;
1334 }
1335
1336 arcan_3dmodel* dst = (arcan_3dmodel*) vobj->feed.state.ptr;
1337
1338 pthread_mutex_lock(&dst->lock);
1339 if (dst->work_count != 0 || !dst->flags.complete){
1340 dst->deferred.scale = true;
1341 pthread_mutex_unlock(&dst->lock);
1342 return ARCAN_OK;
1343 }
1344 struct geometry* geom = dst->geometry;
1345
1346 while (geom){
1347 minmax_verts(&dst->bbmin, &dst->bbmax,
1348 geom->store.verts, geom->store.n_vertices);
1349 geom = geom->next;
1350 }
1351
1352 geom = dst->geometry;
1353 float sf, tx, ty, tz;
1354
1355 float dx = dst->bbmax.x - dst->bbmin.x;
1356 float dy = dst->bbmax.y - dst->bbmin.y;
1357 float dz = dst->bbmax.z - dst->bbmin.z;
1358
1359 if (dz > dy && dz > dx)
1360 sf = 2.0 / dz;
1361 else if (dy > dz && dy > dx)
1362 sf = 2.0 / dy;
1363 else
1364 sf = 2.0 / dx;
1365
1366 dst->bbmax = mul_vectorf(dst->bbmax, sf);
1367 dst->bbmin = mul_vectorf(dst->bbmin, sf);
1368
1369 tx = (0.0 - dst->bbmin.x) - (dst->bbmax.x - dst->bbmin.x) * 0.5;
1370 ty = (0.0 - dst->bbmin.y) - (dst->bbmax.y - dst->bbmin.y) * 0.5;
1371 tz = (0.0 - dst->bbmin.z) - (dst->bbmax.z - dst->bbmin.z) * 0.5;
1372
1373 dst->bbmax.x += tx; dst->bbmin.x += tx;
1374 dst->bbmax.y += ty; dst->bbmin.y += ty;
1375 dst->bbmax.z += tz; dst->bbmin.z += tz;
1376
1377 while(geom){
1378 for (unsigned i = 0; i < geom->store.n_vertices * 3; i += 3){
1379 geom->store.verts[i] = tx + geom->store.verts[i] * sf;
1380 geom->store.verts[i+1] = ty + geom->store.verts[i+1] * sf;
1381 geom->store.verts[i+2] = tz + geom->store.verts[i+2] * sf;
1382 }
1383
1384 geom = geom->next;
1385 }
1386
1387 pthread_mutex_unlock(&dst->lock);
1388 return ARCAN_OK;
1389 }
1390
1391
arcan_3d_infinitemodel(arcan_vobj_id id,bool state)1392 arcan_errc arcan_3d_infinitemodel(arcan_vobj_id id, bool state)
1393 {
1394 arcan_vobject* vobj = arcan_video_getobject(id);
1395 if (!vobj)
1396 return ARCAN_ERRC_NO_SUCH_OBJECT;
1397
1398 if (vobj->feed.state.tag != ARCAN_TAG_3DOBJ)
1399 return ARCAN_ERRC_UNACCEPTED_STATE;
1400
1401 arcan_3dmodel* dstobj = vobj->feed.state.ptr;
1402 dstobj->flags.infinite = state;
1403
1404 return ARCAN_OK;
1405 }
1406
arcan_3d_finalizemodel(arcan_vobj_id id)1407 arcan_errc arcan_3d_finalizemodel(arcan_vobj_id id)
1408 {
1409 arcan_vobject* vobj = arcan_video_getobject(id);
1410 if (!vobj)
1411 return ARCAN_ERRC_NO_SUCH_OBJECT;
1412
1413 if (vobj->feed.state.tag != ARCAN_TAG_3DOBJ)
1414 return ARCAN_ERRC_UNACCEPTED_STATE;
1415
1416 arcan_3dmodel* dstobj = vobj->feed.state.ptr;
1417
1418 if (dstobj->flags.complete == false){
1419 dstobj->flags.complete = true;
1420 push_deferred(dstobj);
1421 }
1422
1423 return ARCAN_OK;
1424 }
1425
arcan_3d_emptymodel()1426 arcan_vobj_id arcan_3d_emptymodel()
1427 {
1428 arcan_vobj_id rv = ARCAN_EID;
1429 img_cons econs = {0};
1430 arcan_3dmodel* newmodel = arcan_alloc_mem(sizeof(arcan_3dmodel),
1431 ARCAN_MEM_VTAG, ARCAN_MEM_BZERO, ARCAN_MEMALIGN_NATURAL);
1432 vfunc_state state = {.tag = ARCAN_TAG_3DOBJ, .ptr = newmodel};
1433
1434 rv = arcan_video_addfobject(FFUNC_3DOBJ, state, econs, 1);
1435
1436 if (rv != ARCAN_EID){
1437 newmodel->parent = arcan_video_getobject(rv);
1438 pthread_mutex_init(&newmodel->lock, NULL);
1439 } else
1440 arcan_mem_free(newmodel);
1441
1442 return rv;
1443 }
1444
arcan_3d_baseorient(arcan_vobj_id dst,float roll,float pitch,float yaw)1445 arcan_errc arcan_3d_baseorient(arcan_vobj_id dst,
1446 float roll, float pitch, float yaw)
1447 {
1448 arcan_vobject* vobj = arcan_video_getobject(dst);
1449
1450 if (!vobj)
1451 return ARCAN_ERRC_NO_SUCH_OBJECT;
1452
1453 if (vobj->feed.state.tag != ARCAN_TAG_3DOBJ)
1454 return ARCAN_ERRC_UNACCEPTED_STATE;
1455
1456 arcan_3dmodel* model = vobj->feed.state.ptr;
1457 pthread_mutex_lock(&model->lock);
1458
1459 if (model->work_count != 0 || !model->flags.complete){
1460 model->deferred.orient = true;
1461 model->deferred.orientf.x = roll;
1462 model->deferred.orientf.y = pitch;
1463 model->deferred.orientf.z = yaw;
1464 pthread_mutex_unlock(&model->lock);
1465 return ARCAN_OK;
1466 }
1467
1468 struct geometry* geom = model->geometry;
1469
1470 /* 1. create the rotation matrix by mapping to a quaternion */
1471 quat repr = build_quat_taitbryan(roll, pitch, yaw);
1472 _Alignas(16) float matr[16];
1473 matr_quatf(repr, matr);
1474
1475 /* 2. iterate all geometries connected to the model */
1476 while (geom){
1477 float* verts = geom->store.verts;
1478
1479 /* 3. sweep through all the vertexes in the model */
1480 for (unsigned i = 0; i < geom->store.n_vertices * 3; i += 3){
1481 _Alignas(16) float xyz[4] = {verts[i], verts[i+1], verts[i+2], 1.0};
1482 _Alignas(16) float out[4];
1483
1484 /* 4. transform the current vertex */
1485 mult_matrix_vecf(matr, xyz, out);
1486 verts[i] = out[0]; verts[i+1] = out[1]; verts[i+2] = out[2];
1487 }
1488
1489 geom = geom->next;
1490 }
1491
1492 pthread_mutex_unlock(&model->lock);
1493 return ARCAN_OK;
1494 }
1495
arcan_3d_camproj(arcan_vobj_id vid,float proj[static16])1496 arcan_errc arcan_3d_camproj(arcan_vobj_id vid, float proj[static 16])
1497 {
1498 arcan_vobject* vobj = arcan_video_getobject(vid);
1499 if (!vobj)
1500 return ARCAN_ERRC_NO_SUCH_OBJECT;
1501
1502 struct camtag_data* camera = vobj->feed.state.ptr;
1503 if (vobj->feed.state.tag != ARCAN_TAG_3DCAMERA)
1504 return ARCAN_ERRC_UNACCEPTED_STATE;
1505
1506 memcpy(camera->projection, proj, sizeof(float) * 16);
1507 return ARCAN_OK;
1508 }
1509
arcan_3d_camtag(arcan_vobj_id tgtid,arcan_vobj_id vid,float near,float far,float ar,float fov,int flags,...)1510 arcan_errc arcan_3d_camtag(arcan_vobj_id tgtid,
1511 arcan_vobj_id vid, float near, float far, float ar, float fov, int flags, ...)
1512 {
1513 arcan_vobject* vobj = arcan_video_getobject(vid);
1514 struct rendertarget* tgt = NULL;
1515 if (tgtid != ARCAN_EID){
1516 arcan_vobject* tgtobj = arcan_video_getobject(tgtid);
1517 tgt = arcan_vint_findrt(tgtobj);
1518 }
1519
1520 va_list vl;
1521 va_start(vl, flags);
1522
1523 if (vobj->feed.state.ptr)
1524 return ARCAN_ERRC_UNACCEPTED_STATE;
1525
1526 if (tgt)
1527 tgt->camtag = vobj->cellid;
1528 else
1529 vobj->owner->camtag = vobj->cellid;
1530
1531 struct camtag_data* camobj = arcan_alloc_mem(
1532 sizeof(struct camtag_data),
1533 ARCAN_MEM_VTAG, ARCAN_MEM_BZERO, ARCAN_MEMALIGN_SIMD
1534 );
1535
1536 camobj->near = near;
1537 camobj->far = far;
1538 camobj->flags = flags;
1539 if (flags & MESH_FILL_LINE){
1540 camobj->line_width = va_arg(vl, double);
1541 }
1542 else
1543 camobj->line_width = 1.0;
1544
1545 build_projection_matrix(camobj->projection, near, far, ar, fov);
1546
1547 vfunc_state state = {.tag = ARCAN_TAG_3DCAMERA, .ptr = camobj};
1548 arcan_video_alterfeed(vid, FFUNC_3DOBJ, state);
1549
1550 FL_SET(vobj, FL_FULL3D);
1551
1552 va_end(vl);
1553 return ARCAN_OK;
1554 }
1555