540 lines
12 KiB
C++
540 lines
12 KiB
C++
|
/*
|
||
|
* Copyright (c) 1993-1995 Argonaut Technologies Limited. All rights reserved.
|
||
|
*
|
||
|
* $Id: zbrendr.c 1.36 1995/08/31 16:48:02 sam Exp $
|
||
|
* $Locker: sam $
|
||
|
*
|
||
|
* Given world, a camera, a vieport and a pixmap - perform the rendering
|
||
|
*/
|
||
|
|
||
|
#include "zb.h"
|
||
|
#include "shortcut.h"
|
||
|
#include "brassert.h"
|
||
|
|
||
|
static char rscid[] = "$Id: zbrendr.c 1.36 1995/08/31 16:48:02 sam Exp $";
|
||
|
|
||
|
/*
|
||
|
* System defaults
|
||
|
*/
|
||
|
extern br_model default_model;
|
||
|
extern br_material default_material;
|
||
|
|
||
|
STATIC void ZbActorRenderOnScreen(br_actor *ap,
|
||
|
br_model *model,
|
||
|
br_material *material,
|
||
|
br_uint_8 style);
|
||
|
|
||
|
/*
|
||
|
* Table of functions to call for each render style
|
||
|
*/
|
||
|
|
||
|
STATIC void (*ZbRenderStyleCalls[])(
|
||
|
br_actor *actor,
|
||
|
br_model *model,
|
||
|
br_material *material,
|
||
|
br_uint_8 style,
|
||
|
int on_screen) = {
|
||
|
ZbMeshRender, /* BR_RSTYLE_DEFAULT */
|
||
|
ZbNullRender, /* BR_RSTYLE_NONE */
|
||
|
ZbMeshRenderPoints, /* BR_RSTYLE_POINTS */
|
||
|
ZbMeshRenderEdges, /* BR_RSTYLE_EDGES */
|
||
|
ZbMeshRender, /* BR_RSTYLE_FACES */
|
||
|
ZbBoundingBoxRenderPoints, /* BR_RSTYLE_BOUNDING_POINTS */
|
||
|
ZbBoundingBoxRenderEdges, /* BR_RSTYLE_BOUNDING_EDGES */
|
||
|
ZbBoundingBoxRenderFaces, /* BR_RSTYLE_BOUNDING_FACES */
|
||
|
};
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Renders a model with current settings
|
||
|
*/
|
||
|
void BR_PUBLIC_ENTRY BrZbModelRender(br_actor *actor,
|
||
|
br_model *model,
|
||
|
br_material *material,
|
||
|
br_uint_8 style,
|
||
|
int on_screen,
|
||
|
int use_custom)
|
||
|
{
|
||
|
UASSERT(fw.rendering);
|
||
|
UASSERT(model != NULL);
|
||
|
UASSERT(material != NULL);
|
||
|
UASSERT(actor != NULL);
|
||
|
|
||
|
UASSERT(model->prep_flags & MODUF_REGISTERED);
|
||
|
|
||
|
/*
|
||
|
* If model has custom callback, invoke that
|
||
|
*/
|
||
|
if(use_custom && (model->flags & BR_MODF_CUSTOM))
|
||
|
model->custom(actor,model,material,NULL,style,on_screen,
|
||
|
&fw.model_to_view, &fw.model_to_screen);
|
||
|
else
|
||
|
ZbRenderStyleCalls[style](actor, model, material, style, on_screen);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Rendering traversal for the given actor
|
||
|
*
|
||
|
*/
|
||
|
STATIC void ZbActorRender(br_actor *ap,
|
||
|
br_model *model,
|
||
|
br_material *material,
|
||
|
br_uint_8 style)
|
||
|
{
|
||
|
/*
|
||
|
* Saved state
|
||
|
*/
|
||
|
br_matrix4 m_to_s;
|
||
|
br_matrix34 m_to_v;
|
||
|
br_matrix34 m_to_e;
|
||
|
br_material *this_material;
|
||
|
br_model *this_model;
|
||
|
br_actor *a;
|
||
|
|
||
|
int s;
|
||
|
|
||
|
/*
|
||
|
* Ignore actors with no children that are not models, and actors with renderstyle = NONE
|
||
|
*/
|
||
|
if(ap->children == NULL && ap->type != BR_ACTOR_MODEL)
|
||
|
return;
|
||
|
|
||
|
if(ap->render_style == BR_RSTYLE_NONE)
|
||
|
return;
|
||
|
|
||
|
/*
|
||
|
* See if this actor overrides default material, model or style
|
||
|
*/
|
||
|
this_material = ap->material?ap->material:material;
|
||
|
this_model = ap->model?ap->model:model;
|
||
|
if(ap->render_style != BR_RSTYLE_DEFAULT)
|
||
|
style = ap->render_style;
|
||
|
|
||
|
/*
|
||
|
* Catch special case of identity transforms
|
||
|
*/
|
||
|
if(ap->t.type == BR_TRANSFORM_IDENTITY) {
|
||
|
/**
|
||
|
** Actor has no transform
|
||
|
**/
|
||
|
switch(ap->type) {
|
||
|
|
||
|
case BR_ACTOR_MODEL:
|
||
|
/*
|
||
|
* This is a model - see if model's bounding box is on screen
|
||
|
*/
|
||
|
if((s = BrOnScreenCheck(&this_model->bounds)) != OSC_REJECT)
|
||
|
BrZbModelRender(ap,this_model,this_material,style,s,1);
|
||
|
break;
|
||
|
|
||
|
case BR_ACTOR_BOUNDS:
|
||
|
/*
|
||
|
* A bounding box - truncate whole tree if rejected
|
||
|
*/
|
||
|
if(BrOnScreenCheck(ap->type_data) == OSC_REJECT)
|
||
|
/* DONT PROCESS CHILDREN */
|
||
|
return;
|
||
|
break;
|
||
|
|
||
|
case BR_ACTOR_BOUNDS_CORRECT:
|
||
|
/*
|
||
|
* A garuanteed bounding box - test to see if it is on screen
|
||
|
*/
|
||
|
switch(BrOnScreenCheck(ap->type_data)) {
|
||
|
|
||
|
case OSC_ACCEPT:
|
||
|
/*
|
||
|
* Bounding box is completely on screen - process children with special loop
|
||
|
*/
|
||
|
BR_FOR_SIMPLELIST(&ap->children, a)
|
||
|
ZbActorRenderOnScreen(a,this_model,this_material,style);
|
||
|
/* FALL THROUGH */
|
||
|
|
||
|
case OSC_REJECT:
|
||
|
/* DONT PROCESS CHILDREN */
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Recurse for children
|
||
|
*/
|
||
|
BR_FOR_SIMPLELIST(&ap->children, a)
|
||
|
ZbActorRender(a,this_model,this_material,style);
|
||
|
|
||
|
} else {
|
||
|
/**
|
||
|
** Actor has a transform
|
||
|
**/
|
||
|
|
||
|
/*
|
||
|
* Save the current transforms
|
||
|
*/
|
||
|
m_to_s = fw.model_to_screen;
|
||
|
m_to_v = fw.model_to_view;
|
||
|
m_to_e = fw.model_to_environment;
|
||
|
|
||
|
/*
|
||
|
* See if this actor is on the camera path - if so, generate a new transform
|
||
|
*/
|
||
|
if(ap == fw.camera_path[ap->depth].a) {
|
||
|
BrMatrix34Inverse(&fw.model_to_view,&fw.camera_path[ap->depth].m);
|
||
|
|
||
|
BrMatrix4Copy(&fw.model_to_screen,&fw.view_to_screen);
|
||
|
BrMatrix4Pre34(&fw.model_to_screen,&fw.model_to_view);
|
||
|
} else {
|
||
|
BrMatrix4PreTransform(&fw.model_to_screen,&ap->t);
|
||
|
BrMatrix34PreTransform(&fw.model_to_view,&ap->t);
|
||
|
}
|
||
|
|
||
|
if(fw.enabled_environment)
|
||
|
BrMatrix34PreTransform(&fw.model_to_environment,&ap->t);
|
||
|
|
||
|
switch(ap->type) {
|
||
|
|
||
|
case BR_ACTOR_MODEL:
|
||
|
/*
|
||
|
* This is a model - see if model's bounding box is on screen
|
||
|
*/
|
||
|
if((s = BrOnScreenCheck(&this_model->bounds)) != OSC_REJECT)
|
||
|
BrZbModelRender(ap,this_model,this_material,style,s,1);
|
||
|
break;
|
||
|
|
||
|
case BR_ACTOR_BOUNDS:
|
||
|
/*
|
||
|
* A bounding box - truncate whole tree if rejected
|
||
|
*/
|
||
|
if(BrOnScreenCheck(ap->type_data) == OSC_REJECT) {
|
||
|
fw.model_to_view = m_to_v;
|
||
|
fw.model_to_screen = m_to_s;
|
||
|
fw.model_to_environment = m_to_e;
|
||
|
return;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case BR_ACTOR_BOUNDS_CORRECT:
|
||
|
/*
|
||
|
* A garuanteed bounding box - test to see if it is on screen
|
||
|
*/
|
||
|
switch(BrOnScreenCheck(ap->type_data)) {
|
||
|
|
||
|
case OSC_ACCEPT:
|
||
|
/*
|
||
|
* Bounding box is completely on screen - process children with special loop
|
||
|
*/
|
||
|
BR_FOR_SIMPLELIST(&ap->children, a)
|
||
|
ZbActorRenderOnScreen(a,this_model,this_material,style);
|
||
|
/* FALL THROUGH */
|
||
|
|
||
|
case OSC_REJECT:
|
||
|
/*
|
||
|
* Don't process children
|
||
|
*/
|
||
|
fw.model_to_view = m_to_v;
|
||
|
fw.model_to_screen = m_to_s;
|
||
|
fw.model_to_environment = m_to_e;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Recurse for children
|
||
|
*/
|
||
|
BR_FOR_SIMPLELIST(&ap->children, a)
|
||
|
ZbActorRender(a,this_model,this_material,style);
|
||
|
|
||
|
/*
|
||
|
* Restore transforms
|
||
|
*/
|
||
|
fw.model_to_view = m_to_v;
|
||
|
fw.model_to_screen = m_to_s;
|
||
|
fw.model_to_environment = m_to_e;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Rendering traversal for an actor that is completely on screen, along
|
||
|
* with any children
|
||
|
*/
|
||
|
STATIC void ZbActorRenderOnScreen(br_actor *ap,
|
||
|
br_model *model,
|
||
|
br_material *material,
|
||
|
br_uint_8 style)
|
||
|
{
|
||
|
/*
|
||
|
* Saved state
|
||
|
*/
|
||
|
br_matrix4 m_to_s;
|
||
|
br_matrix34 m_to_v;
|
||
|
br_matrix34 m_to_e;
|
||
|
br_material *this_material;
|
||
|
br_model *this_model;
|
||
|
br_actor *a;
|
||
|
|
||
|
/*
|
||
|
* Ignore actors with no children that are not models, and actors with renderstyle = NONE
|
||
|
*/
|
||
|
if(ap->children == NULL && ap->type != BR_ACTOR_MODEL)
|
||
|
return;
|
||
|
|
||
|
if(ap->render_style == BR_RSTYLE_NONE)
|
||
|
return;
|
||
|
|
||
|
/*
|
||
|
* See if this actor overrides default material, model or style
|
||
|
*/
|
||
|
this_material = ap->material?ap->material:material;
|
||
|
this_model = ap->model?ap->model:model;
|
||
|
if(ap->render_style != BR_RSTYLE_DEFAULT)
|
||
|
style = ap->render_style;
|
||
|
|
||
|
/*
|
||
|
* Catch special case of identity transforms
|
||
|
*/
|
||
|
if(ap->t.type == BR_TRANSFORM_IDENTITY) {
|
||
|
/*
|
||
|
* This actor has an no transform
|
||
|
*/
|
||
|
if(ap->type == BR_ACTOR_MODEL)
|
||
|
BrZbModelRender(ap,this_model,this_material,style,OSC_ACCEPT,1);
|
||
|
|
||
|
BR_FOR_SIMPLELIST(&ap->children, a)
|
||
|
ZbActorRenderOnScreen(a,this_model,this_material,style);
|
||
|
return;
|
||
|
|
||
|
} else {
|
||
|
/*
|
||
|
* Actor has a transform
|
||
|
*/
|
||
|
m_to_s = fw.model_to_screen;
|
||
|
m_to_v = fw.model_to_view;
|
||
|
m_to_e = fw.model_to_environment;
|
||
|
|
||
|
/*
|
||
|
* See if this actor is on the camera path - if so, generate a new transform
|
||
|
*/
|
||
|
if(ap == fw.camera_path[ap->depth].a) {
|
||
|
/*
|
||
|
* Save model_to_screen and model_to_view
|
||
|
*/
|
||
|
BrMatrix34Inverse(&fw.model_to_view,&fw.camera_path[ap->depth].m);
|
||
|
|
||
|
BrMatrix4Copy(&fw.model_to_screen,&fw.view_to_screen);
|
||
|
BrMatrix4Pre34(&fw.model_to_screen,&fw.model_to_view);
|
||
|
} else {
|
||
|
BrMatrix4PreTransform(&fw.model_to_screen,&ap->t);
|
||
|
BrMatrix34PreTransform(&fw.model_to_view,&ap->t);
|
||
|
}
|
||
|
|
||
|
if(fw.enabled_environment)
|
||
|
BrMatrix34PreTransform(&fw.model_to_environment,&ap->t);
|
||
|
|
||
|
if(ap->type == BR_ACTOR_MODEL)
|
||
|
BrZbModelRender(ap,this_model,this_material,style,OSC_ACCEPT,1);
|
||
|
|
||
|
BR_FOR_SIMPLELIST(&ap->children, a)
|
||
|
ZbActorRenderOnScreen(a,this_model,this_material,style);
|
||
|
|
||
|
/*
|
||
|
* Restore transforms
|
||
|
*/
|
||
|
fw.model_to_view = m_to_v;
|
||
|
fw.model_to_screen = m_to_s;
|
||
|
fw.model_to_environment = m_to_e;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* BrZbSceneRenderBegin()
|
||
|
*
|
||
|
* Setup a new scene to be rendered - processes the camera, lights
|
||
|
* and environment
|
||
|
*/
|
||
|
void BR_PUBLIC_ENTRY BrZbSceneRenderBegin(br_actor *world,
|
||
|
br_actor *camera,
|
||
|
br_pixelmap *colour_buffer,
|
||
|
br_pixelmap *depth_buffer)
|
||
|
{
|
||
|
br_camera *camera_type;
|
||
|
br_matrix34 camera_tfm;
|
||
|
br_actor *a;
|
||
|
int i,j;
|
||
|
|
||
|
UASSERT(zb.type != NULL);
|
||
|
UASSERT(zb.type->colour_type == colour_buffer->type);
|
||
|
UASSERT(zb.type->depth_type == depth_buffer->type);
|
||
|
|
||
|
UASSERT(fw.rendering == 0);
|
||
|
|
||
|
/*
|
||
|
* Set flag to say we are rendering
|
||
|
*/
|
||
|
fw.rendering = 1;
|
||
|
|
||
|
/*
|
||
|
* Constant parameters for renderer
|
||
|
*/
|
||
|
fw.output = colour_buffer;
|
||
|
|
||
|
if(depth_buffer->row_bytes * zb.type->colour_row_size !=
|
||
|
colour_buffer->row_bytes * zb.type->depth_row_size)
|
||
|
BR_ERROR0("Colour and depth strides do not match");
|
||
|
|
||
|
#if 0
|
||
|
/*
|
||
|
* Work out y*row_width table
|
||
|
*/
|
||
|
if(zb.row_width != colour_buffer->row_bytes) {
|
||
|
for(i=0, j=0; i < MAX_OUTPUT_HEIGHT; i++, j+= zb.row_width)
|
||
|
zb.row_table[i] = j;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
zb.row_width = colour_buffer->row_bytes;
|
||
|
zb.depth_row_width = depth_buffer->row_bytes;
|
||
|
|
||
|
|
||
|
zb.colour_buffer = (char *)colour_buffer->pixels+
|
||
|
colour_buffer->base_y*colour_buffer->row_bytes;
|
||
|
|
||
|
|
||
|
zb.depth_buffer = (void *)((char *)depth_buffer->pixels+
|
||
|
depth_buffer->base_y*depth_buffer->row_bytes);
|
||
|
|
||
|
/*
|
||
|
* Parameters of viewport (origin is at pixel centre, so add [0.5,0.5])
|
||
|
*/
|
||
|
if(colour_buffer->width > MAX_OUTPUT_WIDTH ||
|
||
|
colour_buffer->height > MAX_OUTPUT_HEIGHT)
|
||
|
BR_ERROR("BrZbSceneRender: pixelmap is too big");
|
||
|
|
||
|
fw.vp_ox = BR_SCALAR(colour_buffer->base_x+colour_buffer->width/2)+BR_SCALAR(0.5);
|
||
|
fw.vp_width = BR_SCALAR(colour_buffer->width/2);
|
||
|
|
||
|
fw.vp_oy = BR_SCALAR(colour_buffer->height/2)+BR_SCALAR(0.5);
|
||
|
fw.vp_height = -BR_SCALAR(colour_buffer->height/2);
|
||
|
|
||
|
/*
|
||
|
* Work out View Transform from info. in camera actor
|
||
|
*/
|
||
|
fw.vtos_type = BrCameraToScreenMatrix4(&fw.view_to_screen,camera);
|
||
|
|
||
|
/*
|
||
|
* Collect transforms from camera to root
|
||
|
*
|
||
|
* Make a stack of cumulative transforms for each level between
|
||
|
* the camera and the root - this is so that model->view
|
||
|
* transforms can use the shortest route, rather than via the root
|
||
|
*/
|
||
|
for(i=0; i< MAX_CAMERA_DEPTH; i++)
|
||
|
fw.camera_path[i].a = NULL;
|
||
|
|
||
|
i = camera->depth;
|
||
|
a = camera;
|
||
|
BrMatrix34Identity(&fw.camera_path[i].m);
|
||
|
|
||
|
#if 0
|
||
|
if(i > 0) {
|
||
|
fw.camera_path[i].a = a;
|
||
|
BrMatrix34Transform(&fw.camera_path[i].m,&a->t);
|
||
|
a =a->parent;
|
||
|
i --;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
for( ; i > 0; a = a->parent, i--) {
|
||
|
ASSERT(a != NULL);
|
||
|
BrMatrix34Transform(&camera_tfm,&a->t);
|
||
|
BrMatrix34Mul(&fw.camera_path[i-1].m,&fw.camera_path[i].m,&camera_tfm);
|
||
|
fw.camera_path[i].a = a;
|
||
|
}
|
||
|
|
||
|
if(world != a)
|
||
|
BR_ERROR0("camera is not in world hierachy");
|
||
|
|
||
|
/*
|
||
|
* Make world->view as initial model->view
|
||
|
*/
|
||
|
BrMatrix34Inverse(&fw.model_to_view,&fw.camera_path[0].m);
|
||
|
|
||
|
BrMatrix4Copy(&fw.model_to_screen,&fw.view_to_screen);
|
||
|
BrMatrix4Pre34(&fw.model_to_screen,&fw.model_to_view);
|
||
|
|
||
|
/*
|
||
|
* Preprocess active lights, and environment
|
||
|
*/
|
||
|
SurfacePerScene(world,zb.type->true_colour);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* BrZbSceneRenderAdd()
|
||
|
*
|
||
|
* Add a sub-tree of actors to the current rendering
|
||
|
*
|
||
|
*/
|
||
|
void BR_PUBLIC_ENTRY BrZbSceneRenderAdd(br_actor *tree)
|
||
|
{
|
||
|
UASSERT(tree);
|
||
|
UASSERT(fw.rendering);
|
||
|
|
||
|
/*
|
||
|
* Walk the provided world and add transformed models to scene
|
||
|
*/
|
||
|
ZbActorRender(tree, fw.default_model, fw.default_material, BR_RSTYLE_DEFAULT);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* BrZbSceneRenderEnd()
|
||
|
*
|
||
|
* Finish rendering a scene - does nothing, but exists in case we
|
||
|
* ever need a flush of some sort
|
||
|
*/
|
||
|
void BR_PUBLIC_ENTRY BrZbSceneRenderEnd(void)
|
||
|
{
|
||
|
UASSERT(fw.rendering);
|
||
|
fw.rendering = 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* BrZbSceneRender()
|
||
|
*
|
||
|
* Wrapper that sets up, renders and flushes a scene
|
||
|
*
|
||
|
*/
|
||
|
void BR_PUBLIC_ENTRY BrZbSceneRender(br_actor *world,
|
||
|
br_actor *camera,
|
||
|
br_pixelmap *colour_buffer,
|
||
|
br_pixelmap *depth_buffer)
|
||
|
{
|
||
|
BrZbSceneRenderBegin(world, camera, colour_buffer, depth_buffer);
|
||
|
BrZbSceneRenderAdd(world);
|
||
|
BrZbSceneRenderEnd();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Render function for BR_RSTYLE_NONE
|
||
|
*/
|
||
|
STATIC void ZbNullRender(br_actor *actor,
|
||
|
br_model *model,
|
||
|
br_material *material,
|
||
|
br_uint_8 style,
|
||
|
int on_screen)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Set a callback function for bounding rectangles
|
||
|
*/
|
||
|
br_renderbounds_cbfn * BR_PUBLIC_ENTRY BrZbSetRenderBoundsCallback(br_renderbounds_cbfn *new_cbfn)
|
||
|
{
|
||
|
br_renderbounds_cbfn *old_cbfn = zb.bounds_call;
|
||
|
|
||
|
zb.bounds_call = new_cbfn;
|
||
|
|
||
|
return old_cbfn;
|
||
|
}
|
||
|
|