Un-ignored: the dev drive is the ground truth the restoration and emulator work constantly reference (DPL3/LIBDPL + VRENDER i860 renderer source, BT/RP live+dev game trees, VGL_LABS pod boot, scene/audio content). Kept in-repo for the pod-owner community. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2486 lines
60 KiB
C++
2486 lines
60 KiB
C++
|
|
|
|
/*{{{ includes / externs*/
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include "macros.h"
|
|
#include "pazpl5.h"
|
|
#include "pazstore.h"
|
|
#include "matrix.h"
|
|
#include "memclear.h"
|
|
#include "geometry.h"
|
|
#include "lighting.h"
|
|
#include "speclght.h"
|
|
#include "pazread.h"
|
|
#include "names.h"
|
|
#include "viz.h"
|
|
#include "billboard.h"
|
|
#include "render.h"
|
|
|
|
extern int view_id, total_views, back_colour;
|
|
extern concurrency_control *shared_cntl;
|
|
|
|
extern int prev_longest_strip,
|
|
prev_longest_verts,
|
|
prev_longest_tris;
|
|
|
|
extern VSTRIP longest_strip,
|
|
longest_verts,
|
|
longest_tris;
|
|
/*}}} */
|
|
/*{{{ guff + #defines*/
|
|
|
|
/*
|
|
|
|
This is the application interface to PAZ
|
|
----------------------------------------
|
|
|
|
These calls are the only ones visible to the application programmer, and
|
|
are identical for all 3 rendering options, which are
|
|
|
|
i860_8_bit_device software 8-bit
|
|
i860_8_bit_device2 software 8-bit, 2 x i860s
|
|
i860_16_bit_device software 16-bit
|
|
HSP_device hardware 16-bit
|
|
|
|
currently only soft8 and hard16 are supported
|
|
|
|
*/
|
|
|
|
/*}}} */
|
|
|
|
#define messages 0
|
|
/* #define fn_concatenate _concatenate */
|
|
/*{{{ globals*/
|
|
SCENE *currentScene=NULL;
|
|
VIEWRT *vrthead = NULL;
|
|
int PAZerrStatus;
|
|
int fpu_microsex;
|
|
int cull_pass_microsex;
|
|
|
|
/*}}} */
|
|
|
|
static int sect_pixel_req=0;
|
|
static float sect_pixel_x,
|
|
sect_pixel_y;
|
|
static INSTANCE *sectinst=NULL;
|
|
|
|
extern int _processorId,
|
|
_numProcessors;
|
|
static int vrts;
|
|
|
|
/* ok - do this without mallocs per frame */
|
|
/*{{{ static int reorderViews ( SCENE *s )*/
|
|
static int reorderViews ( SCENE *s )
|
|
{
|
|
VIEW *v, *vnext;
|
|
|
|
/*
|
|
printf ("twin-pipe, reorder views\n" );
|
|
*/
|
|
|
|
v=s->eyes.head;
|
|
|
|
if (v) {
|
|
/* printf (" ... got v 0x%x\n", v ); */
|
|
vnext=v->next;
|
|
if (vnext) {
|
|
VIEW *vnextnext=vnext->next;
|
|
/*
|
|
printf (" ... got vnext 0x%x, next 0x%x reorder\n",
|
|
vnext, vnextnext );
|
|
*/
|
|
s->eyes.head=vnext;
|
|
vnext->next=v;
|
|
v->next=vnextnext;
|
|
|
|
if (s->eyes.tail == vnext)
|
|
s->eyes.tail=v;
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
/*}}} */
|
|
#if 1
|
|
/*{{{ static int vrtlist ( char *s )*/
|
|
static int vrtlist ( char *s )
|
|
{
|
|
VIEWRT *vrt;
|
|
VIEW *v;
|
|
int eye, views_for_me=0;
|
|
|
|
/*{{{ free storage for eyes*/
|
|
vrt=vrthead;
|
|
|
|
while (vrt!=NULL) {
|
|
VIEWRT *vrnext=vrt->next;
|
|
|
|
free(vrt);
|
|
vrt=vrnext;
|
|
}
|
|
/*}}} */
|
|
|
|
/*{{{ malloc storage for eyes*/
|
|
/* malloc storage to copy out the eye matrices */
|
|
/* NOW - heres where we can do the LEFT/RIGHT i860 frig */
|
|
/* we only put every other matrix into the VRT list */
|
|
/* ALSO ... now that NAMING is in place, we have to be */
|
|
/* careful about nameToAddress() all over the place */
|
|
|
|
vrthead=NULL;
|
|
|
|
/*
|
|
printf ("%s mallocing views in vrtlist\n", s );
|
|
*/
|
|
|
|
eye=0;
|
|
|
|
if (currentScene) {
|
|
for (v=currentScene->eyes.head; v!=NULL; v=v->next ) {
|
|
if ((eye==view_id)&&(v->enable)) {
|
|
/*
|
|
printf ("adding view 0x%x eye = %d view_id = %d\n", v, eye, view_id );
|
|
*/
|
|
vrt=(VIEWRT *) malloc (sizeof (VIEWRT));
|
|
checkNull(vrt, "Out of heap for view run-time\n");
|
|
vrt->next = vrthead;
|
|
vrt->vista=v;
|
|
v->buffer_B=v->buffer_A;
|
|
vrthead=vrt;
|
|
|
|
views_for_me++;
|
|
}
|
|
eye++;
|
|
|
|
if (eye>total_views) eye=0;
|
|
}
|
|
}
|
|
/*
|
|
printf ("malloced %d views, vrthead=0x%x\n", views_for_me, vrthead);
|
|
*/
|
|
return views_for_me;
|
|
|
|
/*}}} */
|
|
}
|
|
/*}}} */
|
|
#else
|
|
/*{{{ static int vrtlist ( char *s )*/
|
|
static int vrtlist ( char *s )
|
|
{
|
|
VIEWRT *vrt=vrthead,
|
|
*prevrt=NULL;
|
|
VIEW *v;
|
|
|
|
int eye, views_for_me=0;
|
|
|
|
/*{{{ malloc storage for eyes*/
|
|
/* malloc storage to copy out the eye matrices */
|
|
/* NOW - heres where we can do the LEFT/RIGHT i860 frig */
|
|
/* we only put every other matrix into the VRT list */
|
|
/* ALSO ... now that NAMING is in place, we have to be */
|
|
/* careful about nameToAddress() all over the place */
|
|
|
|
/*
|
|
printf ("%s mallocing views in vrtlist\n", s );
|
|
*/
|
|
|
|
eye=0;
|
|
|
|
/* guarantee 1 vrt 1st time round */
|
|
if (vrt==NULL) {
|
|
vrt=(VIEWRT *) malloc (sizeof (VIEWRT));
|
|
vrt->next=NULL;
|
|
vrt->vista=NULL;
|
|
vrthead=vrt;
|
|
}
|
|
|
|
if (currentScene) {
|
|
for (v=currentScene->eyes.head; v!=NULL; v=v->next ) {
|
|
if ((eye==view_id)&&(v->enable)) {
|
|
/*
|
|
printf ("adding view 0x%x eye = %d view_id = %d\n", v, eye, view_id );
|
|
*/
|
|
if (vrt == NULL) {
|
|
vrt=(VIEWRT *) malloc (sizeof (VIEWRT));
|
|
prevrt->next=vrt;
|
|
vrt->next=NULL;
|
|
}
|
|
|
|
vrt->vista=v;
|
|
v->buffer_B=v->buffer_A;
|
|
prevrt=vrt;
|
|
vrt=vrt->next;
|
|
|
|
views_for_me++;
|
|
}
|
|
eye++;
|
|
|
|
if (eye>total_views) eye=0;
|
|
}
|
|
}
|
|
/*
|
|
printf ("malloced %d views, vrthead=0x%x\n", views_for_me, vrthead);
|
|
*/
|
|
|
|
while (vrt->next) {
|
|
vrt=vrt->next;
|
|
vrt->vista=NULL;
|
|
}
|
|
|
|
return views_for_me;
|
|
|
|
/*}}} */
|
|
}
|
|
/*}}} */
|
|
#endif
|
|
/*{{{ matrix calls*/
|
|
|
|
/*{{{ void PAZidMatrix ( MATRIX m )*/
|
|
void PAZidMatrix ( MATRIX m )
|
|
{
|
|
_idmatrix (m);
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZtranslate ( MATRIX m, float x, float y, float z, int post )*/
|
|
void PAZtranslate ( MATRIX m, float x, float y, float z, int post )
|
|
{
|
|
_translate (m,x,y,z,post);
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZrotate ( MATRIX m, float angle, int axis, int post )*/
|
|
void PAZrotate ( MATRIX m, float angle, int axis, int post )
|
|
{
|
|
_rotate (m,angle,axis, post);
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZscale ( MATRIX m, float sx, float sy, float sz, int post )*/
|
|
void PAZscale ( MATRIX m, float sx, float sy, float sz, int post )
|
|
{
|
|
_scale ( m, sx, sy, sz, post );
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZtranslatePair ( MATRIX f, MATRIX b, float x, float y, float z )*/
|
|
void PAZtranslatePair ( MATRIX f, MATRIX b, float x, float y, float z )
|
|
{
|
|
_translate_pair (f,b,x,y,z);
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZrotatePair ( MATRIX f, MATRIX b, float angle, int axis )*/
|
|
void PAZrotatePair ( MATRIX f, MATRIX b, float angle, int axis )
|
|
{
|
|
_rotate_pair (f,b,angle, axis);
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZscalePair ( MATRIX f, MATRIX b, float sx, float sy, float sz )*/
|
|
void PAZscalePair ( MATRIX f, MATRIX b, float sx, float sy, float sz )
|
|
{
|
|
_scale_pair (f, b, sx, sy, sz );
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZconcat ( MATRIX m, MATRIX a, MATRIX b, int post )*/
|
|
void PAZconcat ( MATRIX m, MATRIX a, MATRIX b, int post )
|
|
{
|
|
if (post) fn_concatenate ( m, a, b );
|
|
else fn_concatenate ( m, b, a );
|
|
}
|
|
/*}}} */
|
|
/*}}} */
|
|
/*{{{ object calls*/
|
|
/*{{{ OBJECT *PAZcreateBinObjects ( int *data, OBJECT **objects )*/
|
|
OBJECT *PAZcreateBinObjects ( int *data, OBJECT **objects )
|
|
{
|
|
/*
|
|
OBJECT *o;
|
|
|
|
o = _binread ( data );
|
|
|
|
if (o == NULL) {
|
|
printf (errmess);
|
|
}
|
|
else
|
|
o->instance_count=0;
|
|
return(o);
|
|
*/
|
|
return(NULL);
|
|
}
|
|
/*}}} */
|
|
/*{{{ OBJECT *PAZcreateBinObject ( int *data )*/
|
|
OBJECT *PAZcreateBinObject ( int *data )
|
|
{
|
|
OBJECT *o;
|
|
|
|
o = bin_read ( data );
|
|
|
|
if (o == NULL) {
|
|
printf (errmess);
|
|
}
|
|
else
|
|
o->instance_count=0;
|
|
|
|
return(o);
|
|
}
|
|
/*}}} */
|
|
/*{{{ int PAZdeleteObject ( PAZOBJECT o )*/
|
|
int PAZdeleteObject ( PAZOBJECT o )
|
|
{
|
|
if (o == NULL) {
|
|
printf ("Attempt to delete NULL object\n" );
|
|
exit (666);
|
|
}
|
|
else if (o->instance_count==0) {
|
|
int nl=0, np=0, nv=0, nt=0;
|
|
PATCH *p;
|
|
LOD *l;
|
|
VSTRIP *v;
|
|
VERTEX *t;
|
|
|
|
printf ("delete object 0x%x\n", o );
|
|
|
|
for (l=o->head; p!=NULL; ) {
|
|
LOD *lt=NULL;
|
|
|
|
printf ("delete lod 0x%x\n", l );
|
|
|
|
for (p=l->head; p!=NULL; ) {
|
|
/*{{{ delete each patch*/
|
|
PATCH *pt;
|
|
if (p->patch_type == patch_type_patch) {
|
|
printf ("its a patch\n" );
|
|
for (v=p->head; v!=NULL; ) {
|
|
/*{{{ delete each vstrip*/
|
|
VSTRIP *vt;
|
|
|
|
free ( v->head ); /* the consolidated vstrip */
|
|
|
|
nt+=v->vertex_count;
|
|
|
|
vt=v->next; free(v); v=vt; /* free the vstrip storage */
|
|
|
|
nv++;
|
|
/*}}} */
|
|
}
|
|
}
|
|
else {
|
|
printf ("its a pmesh\n" );
|
|
for (v=p->head; v!=NULL; ) {
|
|
/*{{{ delete each vstrip*/
|
|
VSTRIP *vt;
|
|
|
|
free ( v->head ); /* the consolidated vstrip */
|
|
|
|
nt+=v->vertex_count;
|
|
|
|
vt=v->next; free(v); v=vt; /* free the vstrip storage */
|
|
|
|
nv++;
|
|
/*}}} */
|
|
}
|
|
}
|
|
/*}}} */
|
|
/*{{{ free patch storage + walk*/
|
|
pt=p->next; free(p); p=pt;
|
|
|
|
np++;
|
|
/*}}} */
|
|
}
|
|
/*{{{ free lod storage + walk*/
|
|
lt=l->next; free(l); l=lt;
|
|
|
|
nl++;
|
|
/*}}} */
|
|
}
|
|
|
|
free(o);
|
|
sprintf (errmess, "Deleted %d lods, %d patches, %d vstrips, %d vertices\n", nl, np, nv, nt );
|
|
return (0);
|
|
}
|
|
else
|
|
return(o->instance_count);
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZreadBound ( PAZOBJECT o, POINT* bound )*/
|
|
void PAZreadBound ( PAZOBJECT o, POINT* bound )
|
|
{
|
|
memcpy ( bound, &o->bound, 8*sizeof(POINT));
|
|
}
|
|
/*}}} */
|
|
/*}}} */
|
|
/*{{{ instance calls*/
|
|
|
|
/*{{{ INSTANCE * PAZcreateInstance ()*/
|
|
INSTANCE * PAZcreateInstance ()
|
|
{
|
|
INSTANCE * p=_NewInstance();
|
|
INSTLIST *list=¤tScene->instances;
|
|
|
|
_InitInstance(p);
|
|
|
|
add_item(list, p );
|
|
return(p);
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZdeleteInstance ( INSTANCE * inst )*/
|
|
INSTANCE * PAZdeleteInstance ( INSTANCE * inst )
|
|
{
|
|
INSTLIST *list = ¤tScene->instances;
|
|
OBJECT *obj;
|
|
int i;
|
|
|
|
/* printf ("PAZdeleteInstance 0x%x\n", inst ); */
|
|
|
|
if (inst->obj) {
|
|
obj=(OBJECT *) nameToAddress ((int) inst->obj, viz_createObject );
|
|
|
|
obj->instance_count--;
|
|
if (obj->instance_count < 0) {
|
|
printf ("Error, negative instance count in object 0x%x name 0x%x\n",
|
|
obj, inst->obj );
|
|
exit (666);
|
|
}
|
|
}
|
|
|
|
remove_item ( list, inst, INSTANCE );
|
|
return (list->head);
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZreadInstance ( INSTANCE * local )*/
|
|
void PAZreadInstance ( INSTANCE * local )
|
|
{
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZwriteInstance ( INSTANCE * local )*/
|
|
void PAZwriteInstance ( INSTANCE * local )
|
|
{
|
|
}
|
|
/*}}} */
|
|
|
|
/*}}} */
|
|
/*{{{ intersection calls*/
|
|
/*{{{ about the intersection calls*/
|
|
/*
|
|
The underlying algorithm tries to minimize work by performing intersect
|
|
calculations in the modelling space of the object. There all bounds are
|
|
axis-aligned, so intersecting is trivial. Intersection is done down to
|
|
the patch level. For both calls, a pair of points, one at each end of
|
|
the vector is needed. These points are passed thru the appropriate matrix
|
|
(either back-model concat view, or back-model) before a ray is constructed.
|
|
The ray can be trivially intersected with each plane of the bound. The
|
|
closest intersect is then transformed back into the right space thru the
|
|
forward matrix, to yield a real distance to intersect and a real intersect
|
|
point. The closest intersect encountered is kept, and returned to the
|
|
caller.
|
|
|
|
*/
|
|
|
|
/*}}} */
|
|
/*{{{ static float distance ( POINT start, POINT isect )*/
|
|
static float distance ( POINT start, POINT isect )
|
|
{
|
|
float t, dx, dy, dz;
|
|
|
|
dx = isect[0] - start[0];
|
|
dy = isect[1] - start[1];
|
|
dz = isect[2] - start[2];
|
|
|
|
t = (float) sqrt((dx*dx) + (dy*dy) + (dz*dz));
|
|
|
|
return t;
|
|
}
|
|
/*}}} */
|
|
/*{{{ static int planesect ( POINT isect,*/
|
|
static int planesect ( POINT isect,
|
|
int testaxis,
|
|
float plane,
|
|
POINT start,
|
|
POINT deltas,
|
|
POINT bound[8] )
|
|
{
|
|
float t;
|
|
float delta;
|
|
int axis;
|
|
|
|
/*
|
|
printf ("planesect, axis %d plane %f\n", axis, plane );
|
|
*/
|
|
|
|
/* intersect with plane x=minx */
|
|
|
|
t = (plane-start[testaxis])/deltas[testaxis];
|
|
|
|
if (t < 0.0f)
|
|
return 0;
|
|
|
|
isect[testaxis]=plane;
|
|
|
|
for (axis=0; axis<3; axis++ ) {
|
|
if (testaxis != axis) {
|
|
isect[axis] = start[axis]+(t*deltas[axis]);
|
|
|
|
if (isect[axis] < bound[0][axis])
|
|
return 0;
|
|
if (isect[axis] > bound[7][axis])
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* phew - intersected within bound */
|
|
/*
|
|
printf ("think we got an intersect, at %f,%f,%f\n",
|
|
isect[0], isect[1], isect[2] );
|
|
*/
|
|
|
|
isect[3]=distance ( start, isect );
|
|
|
|
return 1;
|
|
}
|
|
/*}}} */
|
|
/*{{{ static int box_sect ( POINT isect, POINT bound[8], POINT start, POINT pixel )*/
|
|
static int box_sect ( POINT isect, POINT bound[8], POINT start, POINT pixel )
|
|
{
|
|
POINT nearsect;
|
|
float nearest, dx, dy, dz,
|
|
hyp;
|
|
|
|
float nearplane, minx, maxx,
|
|
miny, maxy,
|
|
minz, maxz;
|
|
|
|
POINT deltas;
|
|
|
|
int nearaxis;
|
|
int sects=0;
|
|
|
|
minx=bound[0][0]; miny=bound[0][1]; minz=bound[0][2];
|
|
maxx=bound[7][0]; maxy=bound[7][1]; maxz=bound[7][2];
|
|
|
|
dx=pixel[0]-start[0];
|
|
dy=pixel[1]-start[1];
|
|
dz=pixel[2]-start[2];
|
|
|
|
hyp=(float) sqrt(1.0f/((dx*dx)+(dy*dy)+(dz*dz)));
|
|
|
|
dx*=hyp;
|
|
dy*=hyp;
|
|
dz*=hyp;
|
|
|
|
deltas[0]=dx;
|
|
deltas[1]=dy;
|
|
deltas[2]=dz;
|
|
|
|
if (planesect ( isect, X, minx, pixel, deltas, bound )) {
|
|
/*{{{ */
|
|
sects++;
|
|
if (sects == 1) {
|
|
nearest=isect[3];
|
|
nearsect[0]=isect[0];
|
|
nearsect[1]=isect[1];
|
|
nearsect[2]=isect[2];
|
|
}
|
|
else if (isect[3] < nearest) {
|
|
nearest=isect[3];
|
|
nearsect[0]=isect[0];
|
|
nearsect[1]=isect[1];
|
|
nearsect[2]=isect[2];
|
|
}
|
|
/*}}} */
|
|
}
|
|
if (planesect ( isect, X, maxx, pixel, deltas, bound )) {
|
|
/*{{{ */
|
|
sects++;
|
|
if (sects == 1) {
|
|
nearest=isect[3];
|
|
nearsect[0]=isect[0];
|
|
nearsect[1]=isect[1];
|
|
nearsect[2]=isect[2];
|
|
}
|
|
else if (isect[3] < nearest) {
|
|
nearest=isect[3];
|
|
nearsect[0]=isect[0];
|
|
nearsect[1]=isect[1];
|
|
nearsect[2]=isect[2];
|
|
}
|
|
/*}}} */
|
|
}
|
|
if (planesect ( isect, Y, miny, pixel, deltas, bound )) {
|
|
/*{{{ */
|
|
sects++;
|
|
if (sects == 1) {
|
|
nearest=isect[3];
|
|
nearsect[0]=isect[0];
|
|
nearsect[1]=isect[1];
|
|
nearsect[2]=isect[2];
|
|
}
|
|
else if (isect[3] < nearest) {
|
|
nearest=isect[3];
|
|
nearsect[0]=isect[0];
|
|
nearsect[1]=isect[1];
|
|
nearsect[2]=isect[2];
|
|
}
|
|
/*}}} */
|
|
}
|
|
if (planesect ( isect, Y, maxy, pixel, deltas, bound )) {
|
|
/*{{{ */
|
|
sects++;
|
|
if (sects == 1) {
|
|
nearest=isect[3];
|
|
nearsect[0]=isect[0];
|
|
nearsect[1]=isect[1];
|
|
nearsect[2]=isect[2];
|
|
}
|
|
else if (isect[3] < nearest) {
|
|
nearest=isect[3];
|
|
nearsect[0]=isect[0];
|
|
nearsect[1]=isect[1];
|
|
nearsect[2]=isect[2];
|
|
}
|
|
/*}}} */
|
|
}
|
|
if (planesect ( isect, Z, minz, pixel, deltas, bound )) {
|
|
/*{{{ */
|
|
sects++;
|
|
if (sects == 1) {
|
|
nearest=isect[3];
|
|
nearsect[0]=isect[0];
|
|
nearsect[1]=isect[1];
|
|
nearsect[2]=isect[2];
|
|
}
|
|
else if (isect[3] < nearest) {
|
|
nearest=isect[3];
|
|
nearsect[0]=isect[0];
|
|
nearsect[1]=isect[1];
|
|
nearsect[2]=isect[2];
|
|
}
|
|
/*}}} */
|
|
}
|
|
if (planesect ( isect, Z, maxz, pixel, deltas, bound )) {
|
|
/*{{{ */
|
|
sects++;
|
|
if (sects == 1) {
|
|
nearest=isect[3];
|
|
nearsect[0]=isect[0];
|
|
nearsect[1]=isect[1];
|
|
nearsect[2]=isect[2];
|
|
}
|
|
else if (isect[3] < nearest) {
|
|
nearest=isect[3];
|
|
nearsect[0]=isect[0];
|
|
nearsect[1]=isect[1];
|
|
nearsect[2]=isect[2];
|
|
}
|
|
/*}}} */
|
|
}
|
|
|
|
if (sects == 0)
|
|
return 0;
|
|
else {
|
|
isect[0]=nearsect[0];
|
|
isect[1]=nearsect[1];
|
|
isect[2]=nearsect[2];
|
|
isect[3]=nearest;
|
|
/* printf ("%d intersections, closest %f\n", sects, t ); */
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
/*}}} */
|
|
|
|
/*{{{ statics*/
|
|
static INSTANCE *sectinst, *nearinst;
|
|
static float t, neart;
|
|
static POINT sect, nearsect;
|
|
static int hit;
|
|
/*}}} */
|
|
|
|
/*{{{ static void pix_sect_obj ( OBJECT *obj,*/
|
|
static void pix_sect_obj ( OBJECT *obj,
|
|
MATRIX model,
|
|
MATRIX invModel,
|
|
VIEW *view,
|
|
POINT eye,
|
|
POINT pixel )
|
|
{
|
|
/*
|
|
raytraces thru screen to find instance id of object at
|
|
pixel x, y
|
|
*/
|
|
/* extern fn_concatenate ( MATRIX c, MATRIX a, MATRIX b ); */
|
|
SCENE *s=currentScene;
|
|
LOD *lod=obj->head;
|
|
POINT xfeye, xfpixel, isect;
|
|
VIEWRT *vrt=vrthead;
|
|
|
|
/*{{{ set up matrices JUST 1 EYE in pxpl5*/
|
|
_idmatrix ( vrt->render_f );
|
|
|
|
vrt->render_f [Z][Z]*=-1; /* flip Z-axis, enter viewing coordinate system */
|
|
vrt->render_f [Y][Y]*=-1; /* flip Y-axis to upside the image */
|
|
|
|
fn_concatenate ( vrt->render_f, view->b, vrt->render_f );
|
|
fn_concatenate ( vrt->render_f, model, vrt->render_f );
|
|
|
|
_scale ( vrt->render_f, 1, view->aspect_ratio, 1, 1 );
|
|
|
|
_invert ( vrt->render_b, vrt->render_f );
|
|
|
|
/*}}} */
|
|
|
|
/*
|
|
vrt->render_f moves instance object to screen-space. We
|
|
need eye in the objects coordinate system to ray-trace the
|
|
bounding box.
|
|
|
|
so, invert render_f. pass eye and pixel thru this.
|
|
this lets us build a ray. then intersect this ray with MODELLING
|
|
SPACE bound of object.
|
|
For all bound planes yielding +ve t and inside rectangle, save
|
|
nearest.
|
|
If no intersects, return 0.
|
|
*/
|
|
|
|
/*
|
|
NOTE vrt->render_b moves us to object, so lets get
|
|
endpoints of vector into modelling space
|
|
*/
|
|
_xformpoint ( xfeye, eye, vrt->render_b );
|
|
_xformpoint ( xfpixel, pixel, vrt->render_b );
|
|
|
|
if (box_sect ( isect, lod->bound, xfeye, xfpixel ) != 0) {
|
|
/* do we intersect any patches ? */
|
|
PATCH *p;
|
|
POINT closest;
|
|
int hit=0;
|
|
|
|
for (p=lod->head; p; p=p->next) {
|
|
if (box_sect ( isect, p->bound, xfeye, xfpixel )) {
|
|
if ((hit==0) || (isect[3] < closest[3]))
|
|
memcpy ( closest, isect, sizeof(POINT));
|
|
hit=1;
|
|
}
|
|
}
|
|
if (hit) {
|
|
/* move back to common viewing space to do intersect */
|
|
closest[3]=1.0f;
|
|
|
|
_xformpoint ( xfpixel, closest, vrt->render_f );
|
|
|
|
t=distance ( pixel, xfpixel );
|
|
|
|
if (t < neart) {
|
|
nearinst=sectinst;
|
|
neart=t;
|
|
|
|
_xformpoint ( nearsect, closest, model );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*}}} */
|
|
/*{{{ static void pix_sect_inst ( INSTANCE *root,*/
|
|
static void pix_sect_inst ( INSTANCE *root,
|
|
MATRIX f_parent,
|
|
MATRIX b_parent,
|
|
VIEW *view,
|
|
POINT eye,
|
|
POINT pixel, int nested )
|
|
{
|
|
INSTANCE *inst;
|
|
MATRIX front, back;
|
|
|
|
if (nested > 32) {
|
|
printf ("Are you sure there are 32 heirarchy levels here?\n" );
|
|
return;
|
|
|
|
}
|
|
for (inst=root; inst!=NULL;
|
|
inst=(INSTANCE *) nameToAddress((int)inst->link, viz_createInstance )) {
|
|
|
|
PAZconcat ( front, inst->f, f_parent, 1 );
|
|
PAZconcat ( back, inst->b, b_parent, 0 );
|
|
|
|
if (tree_enable(inst)) {
|
|
pix_sect_inst ( nameToAddress((int) inst->nest, viz_createInstance ),
|
|
front, back, view, eye, pixel, nested+1 );
|
|
}
|
|
|
|
if (inst_enable(inst)) {
|
|
if ((inst->enable & collide_disable) == 0) {
|
|
sectinst=inst;
|
|
|
|
pix_sect_obj ((OBJECT *) (nameToAddress((int) inst->obj,
|
|
viz_createObject)),
|
|
front, back, view,
|
|
eye, pixel );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*}}} */
|
|
/*{{{ INSTANCE *PAZsectPixel ( POINT sect,*/
|
|
INSTANCE *PAZsectPixel ( POINT sect,
|
|
float x, float y, int dot )
|
|
{
|
|
INSTANCE *inst;
|
|
int i;
|
|
VIEW *view;
|
|
MATRIX f, b;
|
|
POINT eye,
|
|
pixel;
|
|
POINT xfeye,
|
|
xfpixel;
|
|
|
|
sectinst=NULL;
|
|
nearinst=NULL;
|
|
neart=10000000.0f;
|
|
|
|
/* printf ("sect pixel %f,%f\n", x, y ); */
|
|
|
|
if (currentScene==NULL)
|
|
return NULL;
|
|
else {
|
|
if (vrthead==NULL)
|
|
return NULL;
|
|
|
|
view=vrthead->vista;
|
|
|
|
if (view==NULL)
|
|
return NULL;
|
|
}
|
|
|
|
eye[0]=0.0f;
|
|
eye[1]=0.0f;
|
|
eye[2]=0.0f;
|
|
eye[3]=1.0f;
|
|
|
|
/* if x==639, maps to 1.0f, 0 maps to -1.0f */
|
|
pixel[0]=((x/view->screen_width) - 0.5f) * 2.0f;
|
|
pixel[1]=((y/view->screen_height) - 0.5f) * 2.0f;
|
|
pixel[2]=view->d;
|
|
pixel[3]=1.0f;
|
|
|
|
pixel[0]-=view->shift_x; /* subtract the shifts */
|
|
pixel[1]-=view->shift_y;
|
|
|
|
for (inst=currentScene->instances.head; inst!=NULL; inst=inst->next ) {
|
|
/*
|
|
this is a bit tacky due to heirarchies - only render if it's
|
|
a) enabled and b) root of a heirarchy
|
|
note that enable is now bit-packed
|
|
*/
|
|
if (inst->enable && (inst->daddy==NULL)) {
|
|
/* for recursive render to work */
|
|
PAZidMatrix(f);
|
|
PAZidMatrix(b);
|
|
|
|
memcpy ( xfeye, eye, 4*sizeof(float));
|
|
memcpy ( xfpixel, pixel, 4*sizeof(float));
|
|
|
|
pix_sect_inst ( inst, f, b, view, xfeye, xfpixel, 1 );
|
|
}
|
|
}
|
|
|
|
/* found it - look it up in nametable */
|
|
|
|
if (nearinst) {
|
|
nearinst = (INSTANCE *) addressToName ( nearinst, viz_createInstance );
|
|
/* printf ("sect, returning 0x%x\n", nearinst ); */
|
|
|
|
if (dot) sect_pixel_req=2;
|
|
|
|
sect_pixel_x=x;
|
|
sect_pixel_y=y;
|
|
|
|
sect[0]=nearsect[0];
|
|
sect[1]=nearsect[1];
|
|
sect[2]=nearsect[2];
|
|
sect[3]=nearsect[3];
|
|
|
|
return nearinst;
|
|
}
|
|
else {
|
|
/* printf ("sect, returning 0x%x\n", nearest ); */
|
|
|
|
return NULL;
|
|
}
|
|
}
|
|
/*}}} */
|
|
|
|
/*{{{ static void vect_sect_obj ( OBJECT *obj,*/
|
|
static void vect_sect_obj ( OBJECT *obj,
|
|
MATRIX model,
|
|
MATRIX invModel,
|
|
POINT eye,
|
|
POINT pixel )
|
|
{
|
|
/*
|
|
raytraces thru screen to find instance id of object at
|
|
pixel x, y
|
|
*/
|
|
/* extern fn_concatenate ( MATRIX c, MATRIX a, MATRIX b ); */
|
|
LOD *lod=obj->head;
|
|
POINT xfeye, xfpixel, isect;
|
|
|
|
_xformpoint ( xfeye, eye, invModel );
|
|
_xformpoint ( xfpixel, pixel, invModel );
|
|
|
|
if (box_sect ( isect, lod->bound, xfeye, xfpixel )) {
|
|
/* do we intersect any patches ? */
|
|
PATCH *p;
|
|
POINT closest;
|
|
int hit=0;
|
|
|
|
for (p=lod->head; p; p=p->next) {
|
|
if (box_sect ( isect, p->bound, xfeye, xfpixel )) {
|
|
if ((hit == 0) || (isect[3] < closest[3]))
|
|
memcpy ( closest, isect, sizeof(POINT));
|
|
hit=1;
|
|
}
|
|
}
|
|
if (hit) {
|
|
/* move back to real space for intersect point */
|
|
closest[3]=1.0f;
|
|
|
|
_xformpoint ( sect, closest, model );
|
|
|
|
t=distance ( pixel, sect );
|
|
|
|
if (t < neart) {
|
|
|
|
nearinst=sectinst;
|
|
neart=t;
|
|
|
|
nearsect[0]=sect[0];
|
|
nearsect[1]=sect[1];
|
|
nearsect[2]=sect[2];
|
|
nearsect[3]=sect[3];
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*}}} */
|
|
/*{{{ static void vect_sect_inst ( INSTANCE *root,*/
|
|
static void vect_sect_inst ( INSTANCE *root,
|
|
MATRIX f_parent,
|
|
MATRIX b_parent,
|
|
POINT eye,
|
|
POINT pixel, int nested )
|
|
{
|
|
INSTANCE *inst;
|
|
MATRIX front, back;
|
|
|
|
if (nested > 32) {
|
|
printf ("Are you sure there are 32 heirarchy levels here?\n" );
|
|
return;
|
|
}
|
|
for (inst=root; inst!=NULL;
|
|
inst=(INSTANCE *) nameToAddress((int)inst->link, viz_createInstance )) {
|
|
|
|
PAZconcat ( front, inst->f, f_parent, 1 );
|
|
PAZconcat ( back, inst->b, b_parent, 0 );
|
|
|
|
if (tree_enable(inst)) {
|
|
vect_sect_inst ( nameToAddress((int) inst->nest, viz_createInstance ),
|
|
front, back, eye, pixel, nested+1 );
|
|
}
|
|
|
|
if (inst_enable(inst)) {
|
|
if ((inst->enable & collide_disable) == 0) {
|
|
sectinst=inst;
|
|
vect_sect_obj ((OBJECT *) (nameToAddress((int) inst->obj,
|
|
viz_createObject)),
|
|
front, back,
|
|
eye, pixel );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*}}} */
|
|
/*{{{ INSTANCE *PAZsectVector ( POINT sect,*/
|
|
INSTANCE *PAZsectVector ( POINT isect,
|
|
float x0, float y0, float z0,
|
|
float x1, float y1, float z1 )
|
|
{
|
|
INSTANCE *inst;
|
|
int i;
|
|
MATRIX f, b;
|
|
POINT eye,
|
|
pixel;
|
|
POINT xfeye,
|
|
xfpixel;
|
|
|
|
sectinst=NULL;
|
|
nearinst=NULL;
|
|
neart=10000000.0f;
|
|
|
|
/*
|
|
printf ("sect vector %f,%f,%f %f,%f,%f\n",
|
|
x0, y0, z0, x1, y1, z1 );
|
|
*/
|
|
if (currentScene==NULL)
|
|
return NULL;
|
|
|
|
eye[0]=x0;
|
|
eye[1]=y0;
|
|
eye[2]=z0;
|
|
eye[3]=1.0f;
|
|
|
|
pixel[0]=x1;
|
|
pixel[1]=y1;
|
|
pixel[2]=z1;
|
|
pixel[3]=1.0f;
|
|
|
|
for (inst=currentScene->instances.head; inst!=NULL; inst=inst->next ) {
|
|
/*
|
|
this is a bit tacky due to heirarchies - only render if it's
|
|
a) enabled and b) root of a heirarchy
|
|
note that enable is now bit-packed
|
|
*/
|
|
if (inst->enable && (inst->daddy==NULL)) {
|
|
/* for recursive render to work */
|
|
PAZidMatrix(f);
|
|
PAZidMatrix(b);
|
|
|
|
memcpy ( xfeye, eye, 4*sizeof(float));
|
|
memcpy ( xfpixel, pixel, 4*sizeof(float));
|
|
|
|
vect_sect_inst ( inst, f, b, xfeye, xfpixel, 1 );
|
|
}
|
|
}
|
|
/* found it - look it up in nametable */
|
|
|
|
if (nearinst) {
|
|
nearinst = (INSTANCE *) addressToName ( nearinst, viz_createInstance );
|
|
/* printf ("sect, returning 0x%x\n", nearinst ); */
|
|
|
|
isect[0]=nearsect[0];
|
|
isect[1]=nearsect[1];
|
|
isect[2]=nearsect[2];
|
|
isect[3]=nearsect[3];
|
|
|
|
return nearinst;
|
|
}
|
|
else {
|
|
/* printf ("sect, returning 0x%x\n", nearinst ); */
|
|
|
|
return NULL;
|
|
}
|
|
}
|
|
/*}}} */
|
|
|
|
/*}}} */
|
|
/*{{{ light calls*/
|
|
|
|
/*{{{ PAZLIGHT PAZcreateLight ()*/
|
|
PAZLIGHT PAZcreateLight ()
|
|
{
|
|
PAZLIGHT p=_NewLight();
|
|
LIGHTLIST *list=¤tScene->lights;
|
|
|
|
_InitLight(p);
|
|
|
|
add_item(list, p );
|
|
|
|
return(p);
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZdeleteLight ( PAZLIGHT light )*/
|
|
PAZLIGHT PAZdeleteLight ( PAZLIGHT light )
|
|
{
|
|
LIGHTLIST *list = ¤tScene->lights;
|
|
remove_item ( list, light, LIGHTSOURCE );
|
|
return (list->head);
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZreadLight ( PAZLIGHT local )*/
|
|
void PAZreadLight ( PAZLIGHT local )
|
|
{
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZwriteLight ( PAZLIGHT local )*/
|
|
void PAZwriteLight ( PAZLIGHT local )
|
|
{
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZinitLight ( PAZLIGHT v, int type,*/
|
|
void PAZinitLight ( PAZLIGHT v, int type,
|
|
float r, float g, float b, float x, float y, float z )
|
|
{
|
|
v->positional=type;
|
|
v->position[0]=x;
|
|
v->position[1]=y;
|
|
v->position[2]=z;
|
|
v->colour[0]=r;
|
|
v->colour[1]=g;
|
|
v->colour[2]=b;
|
|
}
|
|
/*}}} */
|
|
/*}}} */
|
|
/*{{{ viewing calls*/
|
|
|
|
/*{{{ PAZVIEW PAZcreateView ()*/
|
|
|
|
PAZVIEW PAZcreateView ()
|
|
{
|
|
extern poly_pool_list *poly_pool_head;
|
|
|
|
PAZVIEW p=_NewView();
|
|
VIEWLIST *list=¤tScene->eyes;
|
|
PAZVIEW v=p;
|
|
link_poly_list *lp;
|
|
|
|
_InitView(p);
|
|
|
|
#if 0
|
|
/*{{{ NASTY HSP list traversal*/
|
|
|
|
lp=(link_poly_list *) v->buffer_A;
|
|
|
|
/* NEW STUFFF ! ! ! ! ! */
|
|
/*{{{ initialize the view,*/
|
|
{
|
|
poly_pool_list *p=poly_pool_head;
|
|
|
|
while (p->store==NULL) {
|
|
p=p->next;
|
|
}
|
|
|
|
lp->buffer_base=p->store;
|
|
p->store=NULL;
|
|
|
|
/* NOW INIT ALL */
|
|
lp->buffer_used=0;
|
|
lp->buffer_free=lp->buffer_base;
|
|
lp->buffer_check=lp->buffer_base;
|
|
lp->buffer_last=lp->buffer_base;
|
|
}
|
|
/*}}} */
|
|
/* now fragment the storage into a circular list */
|
|
/*{{{ */
|
|
{
|
|
int slots=0;
|
|
/*
|
|
maximum allowed strip to renderer is 16 vertices,
|
|
which requires 16*3+1 words, or 49 words. add one for
|
|
pointer gives 50, so we can fit 81 buffering slots
|
|
into the 16k bytes
|
|
*/
|
|
#define buffer_words 50
|
|
|
|
int *p, words=0;
|
|
p=lp->buffer_base;
|
|
|
|
while ((words+(buffer_words*2)) < 4096) {
|
|
int *prev=p;
|
|
|
|
words+=buffer_words;
|
|
p+=buffer_words;
|
|
prev[0]= (int) p;
|
|
prev[1]=-1;
|
|
slots++;
|
|
|
|
}
|
|
p[0]=(int) lp->buffer_base;
|
|
p[1]=-1;
|
|
|
|
}
|
|
/*}}} */
|
|
/*}}} */
|
|
#endif
|
|
|
|
add_item(list, p );
|
|
|
|
return(p);
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZdeleteView ( PAZVIEW view )*/
|
|
PAZVIEW PAZdeleteView ( PAZVIEW view )
|
|
{
|
|
VIEWLIST *list=¤tScene->eyes;
|
|
extern poly_pool_list *poly_pool_head;
|
|
|
|
/* NEW STUFFFF */
|
|
#if 0
|
|
/*{{{ nasty HSP list traversal*/
|
|
{
|
|
link_poly_list *lp=(link_poly_list *) view->buffer_A;
|
|
poly_pool_list *p=poly_pool_head;
|
|
|
|
while (p->store!=NULL) {
|
|
p=p->next;
|
|
}
|
|
p->store=lp->buffer_base;
|
|
}
|
|
/*}}} */
|
|
#endif
|
|
|
|
if (view->buffer_A) free ( view->buffer_A );
|
|
remove_item ( list, view, VIEW );
|
|
return (list->head);
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZreadView ( PAZVIEW local )*/
|
|
void PAZreadView ( PAZVIEW local )
|
|
{
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZwriteView ( PAZVIEW local )*/
|
|
void PAZwriteView ( PAZVIEW local )
|
|
{
|
|
view_planes ( local, local->shift_x, local->shift_y, local->d );
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZinitView ( PAZVIEW v, float d, float near, float far, float x, float y,*/
|
|
void PAZinitView ( PAZVIEW v, float d, float near, float far, float x, float y,
|
|
float aspect, int device )
|
|
{
|
|
v->d=d;
|
|
v->enable=1;
|
|
v->yon=far;
|
|
v->hither=near;
|
|
|
|
v->screen_width=x;
|
|
v->screen_half_width=x/2;
|
|
|
|
v->screen_height=y;
|
|
v->screen_half_height=y/2;
|
|
|
|
v->device=device;
|
|
|
|
v->shift_x=0;
|
|
v->shift_y=0;
|
|
|
|
v->zscale=near / d;
|
|
v->zmin =near / far;
|
|
|
|
v->zmunge=1.0f / (1.0f - v->zmin);
|
|
v->aspect_ratio = aspect;
|
|
|
|
_idmatrix ( v->f );
|
|
_idmatrix ( v->b );
|
|
|
|
}
|
|
/*}}} */
|
|
/*}}} */
|
|
/*{{{ material calls*/
|
|
/*
|
|
require
|
|
|
|
PAZcreateMaterial
|
|
PAZreadMaterial
|
|
PAZwriteMaterial
|
|
PAZdeleteMaterial
|
|
|
|
*/
|
|
|
|
/*}}} */
|
|
/*{{{ scene calls*/
|
|
|
|
/*{{{ SCENE *PAZcreateScene ()*/
|
|
SCENE *PAZcreateScene ()
|
|
{
|
|
SCENE *s=_NewScene();
|
|
|
|
_InitScene(s);
|
|
return (s);
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZdeleteScene ( SCENE *handle )*/
|
|
void PAZdeleteScene ( SCENE *handle )
|
|
{
|
|
if (handle==NULL) return;
|
|
/*{{{ remove all instances*/
|
|
{
|
|
INSTANCE *p;
|
|
OBJECT *o;
|
|
|
|
for (p=handle->instances.head; p!=NULL; p=handle->instances.head ) {
|
|
int i;
|
|
|
|
o=p->obj;
|
|
|
|
PAZdeleteInstance ( p );
|
|
|
|
if (o!=NULL) {
|
|
PAZdeleteObject ( nameToAddress ((int) o, viz_createObject ));
|
|
}
|
|
}
|
|
}
|
|
/*}}} */
|
|
/*{{{ remove all lights*/
|
|
{
|
|
LIGHTSOURCE *p;
|
|
|
|
for (p=handle->lights.head; p!=NULL; p=handle->lights.head ) {
|
|
PAZdeleteLight ( p );
|
|
}
|
|
}
|
|
/*}}} */
|
|
/*{{{ remove all viewpoints*/
|
|
{
|
|
VIEW *p;
|
|
|
|
for (p=handle->eyes.head; p!=NULL; p=handle->eyes.head ) {
|
|
PAZdeleteView ( p );
|
|
}
|
|
}
|
|
/*}}} */
|
|
free(handle);
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZsetScene ( SCENE *handle )*/
|
|
void PAZsetScene ( SCENE *handle )
|
|
{
|
|
currentScene=handle;
|
|
vrtlist ( "renderScene") ;
|
|
}
|
|
/*}}} */
|
|
|
|
/*}}} */
|
|
/*{{{ control calls*/
|
|
extern quick_renorm ( float *store, float x, float y, float z, float post_scale );
|
|
extern float quick_radius ( float, float, float );
|
|
|
|
int total_grab_patch=0,
|
|
total_grab_light=0;
|
|
|
|
static MATERIAL *f_mtl_override=NULL,
|
|
*b_mtl_override=NULL;
|
|
static TEXTURE *f_tex_override=NULL,
|
|
*b_tex_override=NULL;
|
|
static float sphere_scale;
|
|
static double static_render_strip [((MAX_VERTICES+1) * 56) / 8];
|
|
|
|
/*{{{ void PAZinit ( int device, int x_size, int y_size,*/
|
|
|
|
static int inited=0;
|
|
|
|
void PAZinit ( int device, int x_size, int y_size,
|
|
int device_A, int device_B, int device_C,
|
|
int magic_A, int magic_B, int magic_C, int processor_id )
|
|
{
|
|
|
|
/* mallocs store required for rendering */
|
|
/* plus performs any user-specific initialization */
|
|
|
|
if (inited == 0) { /* should check for processor_id == me */
|
|
inited=1;
|
|
output_device=device;
|
|
|
|
open_fn= (stream_open) &fopen;
|
|
gets_fn= (stream_gets) &fgets;
|
|
close_fn=(stream_close) &fclose;
|
|
|
|
render_strip=(VERTEX *) &static_render_strip[0];
|
|
/* printf ("render_strip at 0x%x\n", render_strip ); */
|
|
|
|
_LinkVertices(render_strip, MAX_VERTICES);
|
|
|
|
render_init( x_size, y_size );
|
|
}
|
|
}
|
|
/*}}} */
|
|
|
|
/*{{{ static void replace_strip ( VSTRIP *strip, int verts )*/
|
|
static void replace_strip ( VSTRIP *strip, int verts )
|
|
{
|
|
int i;
|
|
VERTEX *head,
|
|
*prev=NULL;
|
|
|
|
/*
|
|
printf ("replace_strip, vstrip 0x%x head 0x%x verts %d\n",
|
|
strip, strip->head, verts );
|
|
*/
|
|
if (strip->head)
|
|
free(strip->head);
|
|
|
|
strip->head=_NewVertices(verts);
|
|
if (strip->head) {
|
|
for (head=strip->head, i=0; i<verts; i++) {
|
|
if (prev)
|
|
prev->next=head;
|
|
prev=head;
|
|
head++;
|
|
}
|
|
prev->next=NULL;
|
|
}
|
|
else {
|
|
printf ( "Failed to allocate %d vertices, shared_cntl 0x%x\n",
|
|
verts, shared_cntl );
|
|
}
|
|
|
|
}
|
|
/*}}} */
|
|
/*{{{ on usage of patch / light render records*/
|
|
/*
|
|
These are run-time allocated records which flatten out the
|
|
heirarchical, multiply-instanced datastructures in PAZ to be
|
|
individual patches, with clip-codes and materials.
|
|
|
|
There are 2 types of structure, CHUNKS and RECs. RECs are taken
|
|
out of CHUNKS until the CHUNK is exhausted, when a new CHUNK is allocated.
|
|
|
|
There is an ACTIVE chunk, the chunk from which allocation is currently
|
|
taking place. When the active chunk is exhausted, it has a USAGE and a
|
|
COUNT written into it. The usage is the number of bytes used up in the
|
|
chunk, the count is the number of patches / lights in this chunk. A
|
|
count of 0 indicates the last chunk in the list, or a NULL next field.
|
|
This allows a partial list to be used for rendering, so the length of the
|
|
allocated list is the high water mark.
|
|
*/
|
|
|
|
/*}}} */
|
|
|
|
/*{{{ static patch_render_rec *new_patch_rec(void)*/
|
|
static patch_render_rec *new_patch_rec(void)
|
|
{
|
|
patch_render_rec *rec;
|
|
int usage=shared_cntl->patch_usage;
|
|
int count=shared_cntl->patch_count;
|
|
|
|
if (usage>=MAX_PATCH_USAGE) {
|
|
patch_render_chunk *active=shared_cntl->patch_list_active;
|
|
patch_render_chunk *next;
|
|
|
|
if (active) {
|
|
next=active->next;
|
|
}
|
|
else {
|
|
printf ("SHARED CNTL PATCH_LIST ACTIVE IS NULL, head is 0x%x\n",
|
|
shared_cntl->patch_list_head );
|
|
active=shared_cntl->patch_list_active;
|
|
printf ("re-read active, now 0x%x\n", active );
|
|
printf ("patch_usage now %d\n", shared_cntl->patch_usage );
|
|
printf ("patch_count now %d\n", shared_cntl->patch_count );
|
|
printf ("free_patch_rec 0x%x\n", shared_cntl->free_patch_rec );
|
|
}
|
|
|
|
active->patch_count=count;
|
|
|
|
count=0;
|
|
usage=0;
|
|
|
|
if (next) {
|
|
active=next;
|
|
shared_cntl->patch_list_active=active;
|
|
}
|
|
else {
|
|
next=(patch_render_chunk *) malloc(4096);
|
|
total_grab_patch++;
|
|
if (next==NULL) {
|
|
printf ("newBytes returned NULL in new_patch_rec\n" );
|
|
while(1)
|
|
;
|
|
}
|
|
else {
|
|
active->next=next;
|
|
active=next;
|
|
active->next=NULL;
|
|
shared_cntl->patch_list_active=active;
|
|
}
|
|
}
|
|
shared_cntl->free_patch_rec=&active->patch_data[0];
|
|
}
|
|
|
|
rec=(patch_render_rec *) shared_cntl->free_patch_rec;
|
|
|
|
shared_cntl->free_patch_rec+=patch_bump_val;
|
|
shared_cntl->patch_count = count+1;
|
|
shared_cntl->patch_usage = usage + patch_bump_val;
|
|
|
|
return rec;
|
|
}
|
|
/*}}} */
|
|
/*{{{ static light_render_rec *new_light_rec(void)*/
|
|
static light_render_rec *new_light_rec(void)
|
|
{
|
|
light_render_rec *rec;
|
|
int usage=shared_cntl->light_usage;
|
|
int count=shared_cntl->light_count;
|
|
|
|
if (usage>=MAX_LIGHT_USAGE) {
|
|
light_render_chunk *active=shared_cntl->light_list_active;
|
|
light_render_chunk *next=active->next;
|
|
|
|
count=0;
|
|
usage=0;
|
|
|
|
if (next) {
|
|
active=next;
|
|
shared_cntl->light_list_active=active;
|
|
}
|
|
else {
|
|
next=(light_render_chunk *) malloc(4096);
|
|
total_grab_light++;
|
|
if (next==NULL) {
|
|
printf ("newBytes returned NULL in new_light_rec\n" );
|
|
while(1)
|
|
;
|
|
}
|
|
else {
|
|
active->next=next;
|
|
active=next;
|
|
active->next=NULL;
|
|
shared_cntl->light_list_active=active;
|
|
}
|
|
}
|
|
shared_cntl->free_light_rec=&active->light_data[0];
|
|
}
|
|
|
|
rec=(light_render_rec *) shared_cntl->free_light_rec;
|
|
|
|
shared_cntl->free_light_rec += light_bump_val;
|
|
shared_cntl->light_count = count+1;
|
|
shared_cntl->light_usage = usage+light_bump_val;
|
|
|
|
return rec;
|
|
}
|
|
/*}}} */
|
|
|
|
/*{{{ static void add_renderPatch ( PATCH *p,*/
|
|
static void add_renderPatch ( PATCH *p,
|
|
MATRIX f, MATRIX b,
|
|
LIGHTSOURCE *blonde, VIEW *eye,
|
|
int clip_code,
|
|
patch_fn funky )
|
|
{
|
|
patch_render_rec *prr=new_patch_rec();
|
|
|
|
if (prr) {
|
|
prr->sphere_scale=sphere_scale;
|
|
|
|
matrix_copy ( prr->forward, f );
|
|
matrix_copy ( prr->back, b );
|
|
|
|
InitSem (&prr->sem);
|
|
prr->p=(PATCHISSIMO *) p;
|
|
|
|
prr->blonde = blonde;
|
|
prr->eye = eye;
|
|
prr->clip_code = clip_code;
|
|
prr->rendered = 0;
|
|
prr->render_me = funky;
|
|
|
|
if (f_mtl_override)
|
|
prr->front_mtl=f_mtl_override;
|
|
else
|
|
prr->front_mtl=p->fmaterial;
|
|
|
|
if (b_mtl_override)
|
|
prr->back_mtl=b_mtl_override;
|
|
else
|
|
prr->back_mtl=p->bmaterial;
|
|
|
|
if (f_tex_override)
|
|
prr->front_tex=f_tex_override;
|
|
else if (prr->front_mtl)
|
|
prr->front_tex=prr->front_mtl->tex;
|
|
|
|
if (b_tex_override)
|
|
prr->back_tex=b_tex_override;
|
|
else if (prr->back_mtl)
|
|
prr->back_tex=prr->back_mtl->tex;
|
|
|
|
}
|
|
else {
|
|
printf ("Failed to allocate patch render record\n" );
|
|
}
|
|
}
|
|
/*}}} */
|
|
/*{{{ static float scale_from_matrix ( MATRIX m )*/
|
|
static float scale_from_matrix ( MATRIX m )
|
|
{
|
|
float dx, dy, dz;
|
|
|
|
dx=m[2][0];
|
|
dy=m[2][1];
|
|
dz=m[2][2];
|
|
|
|
return (float) sqrt ((dx*dx) + (dy*dy) + (dz*dz));
|
|
|
|
}
|
|
|
|
/*}}} */
|
|
/*{{{ static LOD *whichLOD ( OBJECT *obj,*/
|
|
static LOD *whichLOD ( OBJECT *obj,
|
|
int *lastLODindex,
|
|
MATRIX model_matrix,
|
|
MATRIX view_matrix )
|
|
{
|
|
/*
|
|
given an object containing a list of LODs, a bounding box and
|
|
a forward matrix, this determines the correct LOD to draw
|
|
|
|
LODs are described as a pair of ranges, within which the LOD is
|
|
valid. For consistency it is necessary to remember between frames
|
|
which LOD we are in (since ranges must overlap for hysteresis).
|
|
|
|
LODs are sorted in ascending order, so the head of the LOD list has
|
|
the smallest value
|
|
*/
|
|
|
|
float LODscale;
|
|
float centrex,
|
|
centrey,
|
|
centrez;
|
|
int hunt, LODindex,
|
|
index=*lastLODindex;
|
|
LOD *lod, *prev=NULL;
|
|
|
|
if (obj == NULL) return NULL;
|
|
|
|
/*{{{ compute LODscale*/
|
|
/*
|
|
compute LODscale
|
|
pass centroid of obj->head thru matrix - centroid is half-way between
|
|
vertex 7 and vertex 0 of bound - note that centroid is held in bound[8]
|
|
*/
|
|
{
|
|
register float px, py, pz;
|
|
|
|
px=0.5f * (obj->bound[7][0]+obj->bound[0][0]);
|
|
py=0.5f * (obj->bound[7][1]+obj->bound[0][1]);
|
|
pz=0.5f * (obj->bound[7][2]+obj->bound[0][2]);
|
|
|
|
centrex=(px * model_matrix[0][0]) +
|
|
(py * model_matrix[1][0]) +
|
|
(pz * model_matrix[2][0]) +
|
|
model_matrix[3][0];
|
|
|
|
centrey=(px * model_matrix[0][1]) +
|
|
(py * model_matrix[1][1]) +
|
|
(pz * model_matrix[2][1]) +
|
|
model_matrix[3][1];
|
|
|
|
centrez=(px * model_matrix[0][2]) +
|
|
(py * model_matrix[1][2]) +
|
|
(pz * model_matrix[2][2]) +
|
|
model_matrix[3][2];
|
|
}
|
|
|
|
LODscale=quick_radius(centrex-view_matrix[3][0],
|
|
centrey-view_matrix[3][1],
|
|
centrez-view_matrix[3][2] );
|
|
|
|
/* printf ( "LODscale of %f for object 0x%x\n", LODscale, obj ); */
|
|
|
|
/*}}} */
|
|
/*{{{ move to previous LOD*/
|
|
|
|
lod=obj->head;
|
|
LODindex=0;
|
|
|
|
hunt=1;
|
|
|
|
while (hunt) {
|
|
if (LODindex == index)
|
|
hunt=0;
|
|
else {
|
|
LODindex++;
|
|
|
|
lod=lod->next;
|
|
if (lod==NULL) {
|
|
hunt= 0;
|
|
lod = obj->tail;
|
|
LODindex--;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*}}} */
|
|
/*{{{ select lod for this frame*/
|
|
|
|
/*
|
|
printf ( "first guess at LOD %d, switch_in %f switch_out %f\n",
|
|
LODindex, lod->switch_in, lod->switch_out );
|
|
*/
|
|
|
|
if (LODscale < lod->switch_in) {
|
|
while (LODscale < lod->switch_in) {
|
|
if (lod->prev==NULL) break;
|
|
else {
|
|
LODindex--;
|
|
lod=lod->prev;
|
|
/* printf ("whichLOD moving to previous rep of 0x%x\n", obj ); */
|
|
}
|
|
}
|
|
}
|
|
else if (LODscale > lod->switch_out) {
|
|
while (LODscale > lod->switch_out) {
|
|
if (lod->next==NULL) return NULL;
|
|
else {
|
|
LODindex++;
|
|
lod=lod->next;
|
|
/* printf ("whichLOD moving to next rep of 0x%x\n", obj ); */
|
|
}
|
|
}
|
|
}
|
|
/*}}} */
|
|
/*{{{ patch data back into instance*/
|
|
|
|
*lastLODindex = LODindex;
|
|
|
|
/*}}} */
|
|
|
|
return lod;
|
|
}
|
|
/*}}} */
|
|
|
|
/*{{{ static int visibility ( POINT *bound, VIEW *v )*/
|
|
static int visibility ( POINT *bound, VIEW *v )
|
|
{
|
|
/*
|
|
we are performing these visibility checks in WORLD space -
|
|
we are checking a frustum, eye at 0,0,0 extending along the -z axis ...
|
|
*/
|
|
#define triv_posses 8
|
|
#define accept_posses 0
|
|
|
|
register int posses, code=0, i;
|
|
|
|
{
|
|
POINT *check;
|
|
register float minz, maxz, pz, hith=v->hither, yo=v->yon;
|
|
|
|
/*{{{ find min+max*/
|
|
check=bound;
|
|
|
|
pz=(*check)[Z];
|
|
minz=pz;
|
|
maxz=minz;
|
|
|
|
/*{{{ 1*/
|
|
check++;
|
|
pz=(*check)[Z];
|
|
if (pz<minz) minz=pz;
|
|
else if (pz>maxz) maxz=pz;
|
|
/*}}} */
|
|
/*{{{ 2*/
|
|
check++;
|
|
pz=(*check)[Z];
|
|
if (pz<minz) minz=pz;
|
|
else if (pz>maxz) maxz=pz;
|
|
/*}}} */
|
|
/*{{{ 3*/
|
|
check++;
|
|
pz=(*check)[Z];
|
|
if (pz<minz) minz=pz;
|
|
else if (pz>maxz) maxz=pz;
|
|
/*}}} */
|
|
/*{{{ 4*/
|
|
check++;
|
|
pz=(*check)[Z];
|
|
if (pz<minz) minz=pz;
|
|
else if (pz>maxz) maxz=pz;
|
|
/*}}} */
|
|
/*{{{ 5*/
|
|
check++;
|
|
pz=(*check)[Z];
|
|
if (pz<minz) minz=pz;
|
|
else if (pz>maxz) maxz=pz;
|
|
/*}}} */
|
|
/*{{{ 6*/
|
|
check++;
|
|
pz=(*check)[Z];
|
|
if (pz<minz) minz=pz;
|
|
else if (pz>maxz) maxz=pz;
|
|
/*}}} */
|
|
/*{{{ 7*/
|
|
check++;
|
|
pz=(*check)[Z];
|
|
if (pz<minz) minz=pz;
|
|
else if (pz>maxz) maxz=pz;
|
|
/*}}} */
|
|
|
|
/*}}} */
|
|
/*{{{ behind me*/
|
|
if (maxz<hith) {
|
|
return(triv_reject);
|
|
}
|
|
/*}}} */
|
|
/*{{{ beyond far clip*/
|
|
else if (minz>yo) {
|
|
return(triv_reject);
|
|
}
|
|
/*}}} */
|
|
|
|
if (maxz>yo) code|=clip_yon;
|
|
if (minz<hith) code|=clip_hither;
|
|
}
|
|
|
|
/*{{{ check y0*/
|
|
posses=fn_bbox_plane ( bound, v->plane_y0 );
|
|
if (posses==triv_posses) {
|
|
return (triv_reject);
|
|
}
|
|
else if (posses != accept_posses) {
|
|
code|=clip_y0;
|
|
}
|
|
/*}}} */
|
|
/*{{{ y1*/
|
|
posses=fn_bbox_plane ( bound, v->plane_y1 );
|
|
if (posses==triv_posses) {
|
|
return (triv_reject);
|
|
}
|
|
else if (posses != accept_posses) {
|
|
code|=clip_y1;
|
|
}
|
|
/*}}} */
|
|
/*{{{ check x0*/
|
|
posses=fn_bbox_plane ( bound, v->plane_x0 );
|
|
|
|
if (posses==triv_posses) {
|
|
return (triv_reject);
|
|
}
|
|
else if (posses != accept_posses) {
|
|
code|=clip_x0;
|
|
}
|
|
/*}}} */
|
|
/*{{{ x1*/
|
|
posses=fn_bbox_plane ( bound, v->plane_x1 );
|
|
|
|
if (posses==triv_posses) {
|
|
return (triv_reject);
|
|
}
|
|
else if (posses != accept_posses) {
|
|
code|=clip_x1;
|
|
}
|
|
/*}}} */
|
|
|
|
return(code);
|
|
}
|
|
/*}}} */
|
|
/*{{{ static void _renderObj ( OBJECT *obj,*/
|
|
static void _renderObj ( OBJECT *obj,
|
|
MATRIX model,
|
|
MATRIX invModel,
|
|
LIGHTSOURCE *lights,
|
|
VIEWRT *vrt,
|
|
int *LODindex,
|
|
int renderMode )
|
|
{
|
|
/* this for fixing Phong (Blinn) half-eye vector */
|
|
#define fix(v) if (((v) > (-0.001)) && ((v) < 0.001)) (v)=0.001
|
|
|
|
POINT xformBound[8],
|
|
*pt=&xformBound[0];
|
|
VIEW *eye=vrt->vista;
|
|
LOD *lod;
|
|
PATCH *p;
|
|
int clip_code;
|
|
int use_properties, i;
|
|
float hyp, *invptr;
|
|
LIGHTSOURCE *light_list=NULL;
|
|
patch_fn funky;
|
|
|
|
if (obj==NULL)
|
|
return;
|
|
|
|
/*{{{ set up matrices JUST 1 EYE in pxpl5*/
|
|
_idmatrix ( vrt->render_f );
|
|
|
|
/* this upside-downs the image, AND inverts the z-coord */
|
|
vrt->render_f [Z][Z]*=-1;
|
|
vrt->render_f [Y][Y]*=-1;
|
|
|
|
fn_concatenate ( vrt->render_f, eye->b, vrt->render_f );
|
|
fn_concatenate ( vrt->render_f, model, vrt->render_f );
|
|
fn_concatenate ( vrt->render_b, eye->f, invModel);
|
|
sphere_scale = scale_from_matrix ( vrt->render_f );
|
|
_scale_pair ( vrt->render_f, vrt->render_b, 1, eye->aspect_ratio, 1 );
|
|
|
|
/*}}} */
|
|
/*{{{ set up level-of-detail, bbox and clip code*/
|
|
/* NB ALWAYS compute lod for inter-eye consistency under clipping */
|
|
lod=whichLOD ( obj, LODindex, model, eye );
|
|
if (lod==NULL) {
|
|
printf ("whichLOD return NULL on object 0x%x\n", obj );
|
|
return;
|
|
}
|
|
|
|
fn_xform_bound ( xformBound, lod->bound, vrt->render_f );
|
|
|
|
clip_code=visibility(xformBound, eye);
|
|
/*}}} */
|
|
|
|
if (clip_code != triv_reject) {
|
|
POINT centroid;
|
|
float phongeye[3];
|
|
|
|
centroid[0]=0.5f * (lod->bound[0][0] + lod->bound[7][0]);
|
|
centroid[1]=0.5f * (lod->bound[0][1] + lod->bound[7][1]);
|
|
centroid[2]=0.5f * (lod->bound[0][2] + lod->bound[7][2]);
|
|
centroid[3]=1.0f;
|
|
|
|
_xformpoint ( centroid, centroid, model );
|
|
|
|
/* phong stuff - malloc, compute and insert phong half-way
|
|
eye vector into each light bulb
|
|
*/
|
|
/*{{{ cook eye with back matrix*/
|
|
/*
|
|
for phong, we need to put eye in definition space also. this is
|
|
done by passing the view vector (0, 0, -1) thru concatenation of
|
|
forward viewing and backward modelling matrix, which is in
|
|
vrt->render_b
|
|
*/
|
|
|
|
invptr=(float *) vrt->render_b;
|
|
|
|
/* in view space, eye pointing in direction { 0, 0, -1 } */
|
|
/* pass this thru matrix ... */
|
|
|
|
/*
|
|
renormalize eye vector - the resulting H vector is scaled by
|
|
power_factor to save multiplies in the shading inner loop
|
|
*/
|
|
|
|
quick_renorm ( phongeye, -invptr[8], -invptr[9], -invptr[10], 1.0f );
|
|
|
|
/*}}} */
|
|
/*{{{ sod the phong - do the light thing*/
|
|
if (lights) {
|
|
/*{{{ how this maths works*/
|
|
/*
|
|
|
|
nasty vector maths frig - in the original phong model, we reflect the
|
|
view vector (from eye to surface) about the normal, and dot that with
|
|
the vector from the normal to the bulb.
|
|
In the H model, we compute the half-way vector between the eye and
|
|
the light bulb - this involves NEGATING the eye - here's why
|
|
|
|
|
|
eye ^ light
|
|
\ | /
|
|
\ | /
|
|
\ | /
|
|
\ | /
|
|
\|/
|
|
|
|
To maximize the highlight in model 1), the eye vector must point at the
|
|
surface, and the light vector must point AT the light. For the H model,
|
|
we must compute H to be the surface normal, which is light-eye.
|
|
|
|
the nasty frig is that the light is a POINT, so has a W coord, so
|
|
we cast the contents of the W coord into a float pointer, and drop
|
|
the H vector in there - jeez, that SUCKS
|
|
*/
|
|
/*}}} */
|
|
|
|
light_render_rec *light_rec, *light_head=NULL;
|
|
LIGHTSOURCE *bulb, *malloc_bulb=NULL, *prev_bulb=NULL;
|
|
|
|
for (bulb=lights; bulb; bulb=bulb->next ) {
|
|
POINT p;
|
|
if (keep_this_bulb ( bulb, centroid, p )) {
|
|
light_rec=new_light_rec();
|
|
|
|
if (light_head==NULL)
|
|
light_head=light_rec;
|
|
|
|
if (light_rec) {
|
|
malloc_bulb=&light_rec->light;
|
|
|
|
memcpy ( malloc_bulb, bulb, sizeof(LIGHTSOURCE));
|
|
|
|
malloc_bulb->colour[0]=p[3]*bulb->colour[0];
|
|
malloc_bulb->colour[1]=p[3]*bulb->colour[1];
|
|
malloc_bulb->colour[2]=p[3]*bulb->colour[2];
|
|
|
|
malloc_bulb->position[0]=p[0];
|
|
malloc_bulb->position[1]=p[1];
|
|
malloc_bulb->position[2]=p[2];
|
|
|
|
malloc_bulb->positional=light_directional;
|
|
malloc_bulb->next=NULL;
|
|
|
|
if (prev_bulb)
|
|
prev_bulb->next=malloc_bulb;
|
|
|
|
prev_bulb=malloc_bulb;
|
|
|
|
if (bulb->positional==light_directional) {
|
|
float **nasty=(float **) malloc_bulb->xformpos;
|
|
register float a, b, c;
|
|
|
|
nasty[3]=&(light_rec->Hvec[0]);
|
|
|
|
a=bulb->xformpos[0]-phongeye[0];
|
|
b=bulb->xformpos[1]-phongeye[1];
|
|
c=bulb->xformpos[2]-phongeye[2];
|
|
|
|
fix (a);
|
|
fix (b);
|
|
fix (c);
|
|
|
|
quick_renorm ( nasty[3], a, b, c, phong_precision * 0.99f );
|
|
}
|
|
}
|
|
else {
|
|
printf ( "Failed to allocate specular light bulb\n" );
|
|
}
|
|
}
|
|
}
|
|
if (light_head)
|
|
light_list=&light_head->light;
|
|
else
|
|
light_list=NULL;
|
|
}
|
|
/*}}} */
|
|
/*{{{ transform lights via back modelling matrix*/
|
|
|
|
if (light_list)
|
|
xform_lights ( light_list, invModel );
|
|
|
|
/*}}} */
|
|
|
|
for (p=lod->head;p!=NULL; p=p->next ) {
|
|
if (p->patch_type == patch_type_patch)
|
|
funky=&renderPatch;
|
|
else if (p->patch_type == patch_type_pmesh)
|
|
funky=&renderPmesh;
|
|
else
|
|
funky=&renderSmesh;
|
|
|
|
if (clip_code == 0) {
|
|
add_renderPatch ( p,
|
|
vrt->render_f, vrt->render_b,
|
|
light_list, eye, 0,
|
|
funky );
|
|
}
|
|
else if (clip_code != triv_reject) {
|
|
int pcc;
|
|
|
|
fn_xform_bound ( xformBound, p->bound, vrt->render_f );
|
|
|
|
if ((pcc=visibility(xformBound, eye)) != triv_reject) {
|
|
add_renderPatch ( p,
|
|
vrt->render_f, vrt->render_b,
|
|
light_list, eye,
|
|
pcc, funky );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*}}} */
|
|
|
|
/*{{{ static void render_inst ( INSTANCE *inst, VIEW *view, LIGHT *lights, MATRIX f, MATRIX b )*/
|
|
static void render_inst ( INSTANCE *root, LIGHTSOURCE *lights,
|
|
MATRIX f_parent, MATRIX b_parent, int nested )
|
|
{
|
|
INSTANCE *inst;
|
|
MATRIX front, back;
|
|
|
|
if (nested > 32) {
|
|
printf ("Are you sure there are 32 heirarchy levels here?\n" );
|
|
return;
|
|
|
|
}
|
|
for (inst=root; inst!=NULL;
|
|
inst=(INSTANCE *) nameToAddress((int)inst->link, viz_createInstance )) {
|
|
|
|
if (inst->enable & dirty_mtx) {
|
|
_invert ( inst->b, inst->f );
|
|
inst->enable^=dirty_mtx;
|
|
}
|
|
|
|
PAZconcat ( front, inst->f, f_parent, 1 );
|
|
PAZconcat ( back, inst->b, b_parent, 0 );
|
|
|
|
if (tree_enable(inst)) {
|
|
render_inst ( nameToAddress((int) inst->nest, viz_createInstance ),
|
|
lights, front, back, nested+1 );
|
|
}
|
|
|
|
if (inst_enable(inst)) {
|
|
/* hack, should be parameters */
|
|
if (inst->obj) {
|
|
f_mtl_override=(MATERIAL *) nameToAddress((int)inst->f_material,
|
|
viz_createMaterial );
|
|
b_mtl_override=(MATERIAL *) nameToAddress((int)inst->b_material,
|
|
viz_createMaterial );
|
|
|
|
f_tex_override=(TEXTURE *) nameToAddress((int)inst->f_texture,
|
|
viz_texture );
|
|
b_tex_override=(TEXTURE *) nameToAddress((int)inst->b_texture,
|
|
viz_texture );
|
|
|
|
if (inst->billboard) {
|
|
billboardize ( front, back );
|
|
}
|
|
|
|
_renderObj ( (OBJECT *) (nameToAddress((int) inst->obj, viz_createObject)),
|
|
front, back,
|
|
lights, vrthead,
|
|
&inst->lastLODindex,
|
|
0 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*}}} */
|
|
/*{{{ static int build_displaylist ( LIGHTSOURCE *non_ambient )*/
|
|
static int build_displaylist ( LIGHTSOURCE *non_ambient )
|
|
{
|
|
SCENE *s=currentScene;
|
|
INSTANCE *inst;
|
|
MATRIX f, b;
|
|
int then, now, insts=0;
|
|
|
|
dN_timer(&then);
|
|
|
|
for (inst=s->instances.head; inst!=NULL; inst=inst->next ) {
|
|
/*
|
|
this is a bit tacky due to heirarchies - only render if it's
|
|
a) enabled and b) root of a heirarchy
|
|
note that enable is now bit-packed
|
|
*/
|
|
if (inst->enable && (inst->daddy==NULL)) {
|
|
/* for recursive render to work */
|
|
PAZidMatrix(f);
|
|
PAZidMatrix(b);
|
|
|
|
render_inst ( inst, non_ambient, f, b, 1 );
|
|
insts++;
|
|
}
|
|
}
|
|
dN_timer(&now);
|
|
|
|
return (now-then);
|
|
}
|
|
/*}}} */
|
|
/*{{{ void PAZrenderScene ( int me, int nodes )*/
|
|
/*
|
|
|
|
PAZrenderScene for pxpl5
|
|
|
|
rendering is now peformed in 2 passes - an initial cull pass
|
|
is performed which contructs a displaylist of visible patches/pmeshes
|
|
this displaylist is then transformed into pxpl5 coefficients by the
|
|
pair of i860s chasing each other's tails.
|
|
|
|
multi-processor issues - it would be really cool to get one processor
|
|
to cull the left eye while the other culls the right eye. This should
|
|
halve the cull time of stereo on a single boardset.
|
|
*/
|
|
|
|
static int reordered=0;
|
|
extern MATERIAL *mdefault;
|
|
|
|
void PAZrenderScene ( int me, int nodes )
|
|
{
|
|
|
|
SCENE *s=currentScene;
|
|
POINT sect;
|
|
INSTANCE *inst, *sectinst=NULL;
|
|
MATERIAL *fm, *bm;
|
|
LIGHTSOURCE *non_ambient=reorderLights ( &s->lights.head );
|
|
int views_for_me;
|
|
|
|
checkNull(currentScene, "Attempt to render NULL scene\n");
|
|
|
|
/*{{{ re-allocate strips if they have grown*/
|
|
/* do some tracing and ultimately some rearranging */
|
|
|
|
if (shared_cntl->length_longest_strip > prev_longest_strip) {
|
|
prev_longest_strip=shared_cntl->length_longest_strip;
|
|
/*
|
|
if (_processorId==0)
|
|
printf ("longest strip got bigger, %d\n", prev_longest_strip );
|
|
*/
|
|
|
|
replace_strip ( &longest_strip, prev_longest_strip );
|
|
}
|
|
|
|
if (shared_cntl->length_longest_verts > prev_longest_verts) {
|
|
prev_longest_verts=shared_cntl->length_longest_verts;
|
|
/*
|
|
if (_processorId==0)
|
|
printf ("longest verts got bigger, %d\n", prev_longest_verts );
|
|
*/
|
|
|
|
replace_strip ( &longest_verts, prev_longest_verts );
|
|
}
|
|
|
|
if (shared_cntl->length_longest_tris > prev_longest_tris) {
|
|
prev_longest_tris=shared_cntl->length_longest_tris;
|
|
/*
|
|
if (_processorId==0)
|
|
printf ("longest tris got bigger, %d\n", prev_longest_tris );
|
|
*/
|
|
|
|
replace_strip ( &longest_tris, prev_longest_tris );
|
|
}
|
|
/*}}} */
|
|
|
|
views_for_me=vrtlist ( "renderScene") ;
|
|
|
|
if (reordered == 0) {
|
|
if (views_for_me == 1) {
|
|
reorderViews(s);
|
|
reordered=1;
|
|
}
|
|
}
|
|
|
|
if (_processorId == 0) {
|
|
resolve_scene (s);
|
|
XP_flush();
|
|
}
|
|
|
|
billboardstuff(s->eyes.head);
|
|
step_fx();
|
|
|
|
cull_pass_microsex=build_displaylist ( non_ambient );
|
|
|
|
render_displaylist ( 1 );
|
|
|
|
if (views_for_me==2) {
|
|
VIEWRT *pushvrt=vrthead;
|
|
|
|
/* swap vrt 0 and vrt 1 */
|
|
vrthead=pushvrt->next;
|
|
|
|
cull_pass_microsex+=build_displaylist ( non_ambient );
|
|
render_displaylist ( 0 );
|
|
|
|
vrthead=pushvrt;
|
|
}
|
|
|
|
flip_screens ( me, nodes );
|
|
}
|
|
/*}}} */
|
|
|
|
/*{{{ void PAZsetBackGND ()*/
|
|
float PAZback_r,
|
|
PAZback_g,
|
|
PAZback_b;
|
|
|
|
void PAZsetBackGND ( float r, float g, float b )
|
|
{
|
|
extern int eof_backR;
|
|
extern int eof_backG;
|
|
extern int eof_backB;
|
|
/*{{{ */
|
|
#if messages
|
|
printf ( "PAZsetBackgnd %f %f %f\n", r, g, b );
|
|
#endif
|
|
/*}}} */
|
|
|
|
PAZback_r = 255.9f * r;
|
|
PAZback_g = 255.9f * g;
|
|
PAZback_b = 255.9f * b;
|
|
|
|
if (output_device == i860_8_bit_device) {
|
|
back_colour = 256*r;
|
|
}
|
|
else if (output_device == i860_SV_device) {
|
|
unsigned int ir=255.9*r, ig=255.9*g, ib=255.9*b;
|
|
|
|
back_colour = (ib<<8) | (ig << 16) | (ir << 24);
|
|
}
|
|
else {
|
|
int ir=255.9*r, ig=255.9*g, ib=255.9*b;
|
|
|
|
ir = (ir | (ig << 8) | (ib << 16));
|
|
|
|
if (ir == 0) {
|
|
back_colour = 0xc0000000; /* -ve, yields 0 when negated and masked */
|
|
}
|
|
else
|
|
back_colour = -ir;
|
|
/*{{{ patch in backgnd colour*/
|
|
|
|
eof_backR = (0xff & (-back_colour));
|
|
eof_backG = (0xff & ((-back_colour) >> 8));
|
|
eof_backB = (0xff & ((-back_colour) >> 16));
|
|
|
|
/*}}} */
|
|
}
|
|
}
|
|
/*}}} */
|
|
|
|
/*}}} */
|
|
/*{{{ heirarchy support*/
|
|
|
|
/*{{{ INSTANCE *PAZlink ( INSTANCE *parent, INSTANCE *chain )*/
|
|
INSTANCE *PAZlink ( INSTANCE *parent, INSTANCE *chain )
|
|
{
|
|
if (parent->link == NULL) {
|
|
parent->link=chain;
|
|
|
|
chain->daddy=parent;
|
|
return(chain);
|
|
}
|
|
else
|
|
return(NULL);
|
|
}
|
|
/*}}} */
|
|
/*{{{ INSTANCE *PAZnest ( INSTANCE *parent, INSTANCE *child )*/
|
|
INSTANCE *PAZnest ( INSTANCE *parent, INSTANCE *child )
|
|
{
|
|
if (parent->nest == NULL) {
|
|
parent->nest=child;
|
|
|
|
child->daddy=parent;
|
|
return(child);
|
|
}
|
|
else
|
|
return(NULL);
|
|
}
|
|
/*}}} */
|
|
|
|
/*{{{ INSTANCE *PAZunlink ( INSTANCE *item )*/
|
|
INSTANCE *PAZunlink ( INSTANCE *item )
|
|
{
|
|
INSTANCE *parent=item->daddy;
|
|
|
|
if (parent != NULL) {
|
|
if (parent->link == item) {
|
|
parent->link=NULL;
|
|
item->daddy=NULL;
|
|
}
|
|
}
|
|
return (parent);
|
|
}
|
|
/*}}} */
|
|
/*{{{ INSTANCE *PAZunnest ( INSTANCE *item )*/
|
|
INSTANCE *PAZunnest ( INSTANCE *item )
|
|
{
|
|
INSTANCE *parent=item->daddy;
|
|
|
|
if (parent != NULL) {
|
|
if (parent->nest == item) {
|
|
parent->nest=NULL;
|
|
item->daddy=NULL;
|
|
}
|
|
}
|
|
return (parent);
|
|
}
|
|
/*}}} */
|
|
|
|
/*{{{ void PAZdeleteTree ( INSTANCE * root )*/
|
|
void PAZdeleteTree ( INSTANCE * root )
|
|
{
|
|
if (root == NULL)
|
|
return;
|
|
else {
|
|
OBJECT *o=root->obj;
|
|
int i;
|
|
|
|
PAZdeleteTree ( root->nest );
|
|
PAZdeleteTree ( root->link );
|
|
PAZdeleteInstance ( root );
|
|
|
|
if (o->instance_count==0)
|
|
PAZdeleteObject ( o );
|
|
}
|
|
}
|
|
/*}}} */
|
|
|
|
/*
|
|
NAME versions of these - the whole tree is now stored as names
|
|
and the functions ALWAYS take names as arguments
|
|
*/
|
|
/*{{{ int PAZlinkName ( int nparent, int nchain )*/
|
|
int PAZlinkName ( int nparent, int nchain )
|
|
{
|
|
INSTANCE *parent=nameToAddress( nparent, viz_createInstance );
|
|
INSTANCE *chain =nameToAddress( nchain, viz_createInstance );
|
|
|
|
if (parent->link == NULL) {
|
|
parent->link=(INSTANCE *) nchain;
|
|
|
|
chain->daddy=(INSTANCE *) nparent;
|
|
return(nchain);
|
|
}
|
|
else
|
|
return((int) NULL);
|
|
}
|
|
/*}}} */
|
|
/*{{{ int PAZnestName ( int nparent, int nchild )*/
|
|
int PAZnestName ( int nparent, int nchild )
|
|
{
|
|
INSTANCE *parent=nameToAddress( nparent, viz_createInstance );
|
|
INSTANCE *child =nameToAddress( nchild, viz_createInstance );
|
|
|
|
if (parent->nest == NULL) {
|
|
parent->nest=(INSTANCE *) nchild;
|
|
|
|
child->daddy=(INSTANCE *) nparent;
|
|
return(nchild);
|
|
}
|
|
else
|
|
return((int) NULL);
|
|
}
|
|
/*}}} */
|
|
|
|
/*{{{ int PAZunlinkName ( int nitem )*/
|
|
int PAZunlinkName ( int nitem )
|
|
{
|
|
INSTANCE *item =(INSTANCE *) nameToAddress( nitem, viz_createInstance );
|
|
int nparent= (int) item->daddy;
|
|
INSTANCE *parent=(INSTANCE *) nameToAddress(nparent, viz_createInstance );
|
|
|
|
if (parent != NULL) {
|
|
if ((int) parent->link == nitem) {
|
|
parent->link=NULL;
|
|
item->daddy=NULL;
|
|
}
|
|
}
|
|
return ( nparent );
|
|
}
|
|
/*}}} */
|
|
/*{{{ int PAZunnestName ( int nitem )*/
|
|
int PAZunnestName ( int nitem )
|
|
{
|
|
INSTANCE *item =(INSTANCE *) nameToAddress( nitem, viz_createInstance );
|
|
int nparent= (int) item->daddy;
|
|
INSTANCE *parent=(INSTANCE *) nameToAddress(nparent, viz_createInstance );
|
|
|
|
if (parent != NULL) {
|
|
if ((int) parent->nest == nitem) {
|
|
parent->nest=NULL;
|
|
item->daddy=NULL;
|
|
}
|
|
}
|
|
return ( nparent );
|
|
}
|
|
/*}}} */
|
|
|
|
/*{{{ void PAZdeleteTreeName ( int nroot )*/
|
|
void PAZdeleteTreeName ( int nroot )
|
|
{
|
|
if (nroot == 0)
|
|
return;
|
|
else {
|
|
INSTANCE *root=nameToAddress ( nroot, viz_createInstance );
|
|
int objname=(int) root->obj, childname;
|
|
OBJECT *o=nameToAddress( objname, viz_createObject );
|
|
|
|
childname = (int) root->nest;
|
|
if (childname) PAZdeleteTreeName ( childname );
|
|
|
|
childname = (int) root->link;
|
|
if (childname) PAZdeleteTreeName ( childname );
|
|
|
|
PAZdeleteInstance ( root );
|
|
|
|
deleteName ( nroot, viz_createInstance );
|
|
|
|
if (o) {
|
|
if (PAZdeleteObject ( o ) == 0)
|
|
deleteName ( objname, viz_createObject );
|
|
}
|
|
}
|
|
}
|
|
/*}}} */
|
|
/*}}} */
|
|
|