Files
CydandClaude Fable 5 db7745fcd0 sda4: commit the Glaze developer hard-drive dump
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>
2026-07-04 19:41:15 -05:00

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=&currentScene->instances;
_InitInstance(p);
add_item(list, p );
return(p);
}
/*}}} */
/*{{{ void PAZdeleteInstance ( INSTANCE * inst )*/
INSTANCE * PAZdeleteInstance ( INSTANCE * inst )
{
INSTLIST *list = &currentScene->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=&currentScene->lights;
_InitLight(p);
add_item(list, p );
return(p);
}
/*}}} */
/*{{{ void PAZdeleteLight ( PAZLIGHT light )*/
PAZLIGHT PAZdeleteLight ( PAZLIGHT light )
{
LIGHTLIST *list = &currentScene->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=&currentScene->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=&currentScene->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 );
}
}
}
/*}}} */
/*}}} */