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