Continue now with the cBarrier
class declaration:
When you need to assign a mesh
to a barrier, use the set_mesh function, passing the
barrier’s identification number to set, as well as cMesh objects to use.
For setting an animation for a barrier, you pass the barrier’s
identification number, cAnimation object, the name of the animation to use, and
the
time the animation is set (using a timer function such as timeGetTime).
You need to assign meshes and
animations.
Each barrier has a dedicated cObject to use for orientation, but first a mesh
must be
assigned via the set_mesh function. Animations are sure to follow using the
set_anim
function. Take a look at each of the functions responsible for setting those
meshes
and animations.
After you've loaded or created
barriers, assign the meshes by their respective barrier
identification numbers. Notice that the set_anim function is just like the
cObject::set_anim_set function—you have the name of the animation and the
starting
time of the animation.
Both functions simply scan
through the linked list of barriers looking for a matching
identification number.
void set_mesh(long id, cMesh* mesh)
{
for(sBarrier* bar = m_root_barrier; bar != NULL; bar = bar->next)
{
if(bar->id == id)
bar->object.create(mesh);
}
}
void set_anim(long id, cAnimation* anim, pcstr name, long start_time)
{
for(sBarrier* bar = m_root_barrier; bar != NULL; bar = bar->next)
{
if(bar->id == id)
bar->object.set_anim_set(anim, name, start_time);
}
}
After you assign a mesh and
animation, you can render a barrier to the display using
the following render function. The render function takes as arguments the
current
time to update the animations (again using timeGetTime) and the viewing frustum
to
use for clipping out the barriers that are out of the viewpoint.
The only other exclusive function
in cBarrier (as opposed to cTrigger) is Render,
which takes a time value that is used to update the barriers’ animations and a
viewing
frustum that is used to clip out unseen barrier objects. Take a look at the
render
function code:
void cBarrier::render(ulong time_now, cFrustum* frustum)
{
for(sBarrier* bar = m_root_barrier; bar != NULL; bar = bar->next)
{
cObject* object = &bar->object;
float radius;
object->get_bounds(NULL, NULL, NULL, NULL, NULL, NULL, &radius);
if(frustum->is_sphere_in(bar->x_pos, bar->y_pos, bar->z_pos, radius))
{
object->move(bar->x_pos, bar->y_pos, bar->z_pos);
object->rotate(bar->x_rot, bar->y_rot, bar->z_rot);
object->update_anim(time_now, true);
object->render();
}
}
}
In the preceding render
function, the linked list of barriers is scanned, and for each
barrier, a frustum check is performed. If a barrier is in the view, its
respective animation
is updated and the mesh is rendered.
When it comes time to start
adding barriers to the linked list, the cBarrier class
comes packed with as many functions to do so as cTrigger.
public:
void add_sphere(long id, bool enabled,
float x_pos, float y_pos, float z_pos,
float x_rot, float y_rot, float z_rot,
float cx_pos, float cy_pos, float cz_pos,
float radius)
{
sBarrier* bar = add_barrier(BARRIER_SPHERE, id, enabled, x_pos, y_pos, z_pos, x_rot, y_rot, z_rot);
bar->x1 = cx_pos;
bar->y1 = cy_pos;
bar->z1 = cz_pos;
bar->radius = radius * radius;
}
void add_box(long id, bool enabled,
float x_pos, float y_pos, float z_pos,
float x_rot, float y_rot, float z_rot,
float x_min, float y_min, float z_min,
float x_max, float y_max, float z_max)
{
sBarrier* bar = add_barrier(BARRIER_SPHERE, id, enabled, x_pos, y_pos, z_pos, x_rot, y_rot, z_rot);
// setup barrier data (fix for min/max values)
bar->x1 = min(x_min, x_max);
bar->y1 = min(y_min, y_max);
bar->z1 = min(z_min, z_max);
bar->x2 = max(x_min, x_max);
bar->y2 = max(y_min, y_max);
bar->z2 = max(z_min, z_max);
}
void add_cylinder(long id, bool enabled,
float x_pos, float y_pos, float z_pos,
float x_rot, float y_rot, float z_rot,
float cx_pos, float cy_pos, float cz_pos,
float radius, float height)
{
sBarrier* bar = add_barrier(BARRIER_SPHERE, id, enabled, x_pos, y_pos, z_pos, x_rot, y_rot, z_rot);
bar->x1 = cx_pos;
bar->y1 = cy_pos;
bar->z1 = cz_pos;
bar->radius = radius * radius;
bar->y2 = height;
}
void add_triangle(long id, bool enabled,
float x_pos, float y_pos, float z_pos,
float x_rot, float y_rot, float z_rot,
float x1, float z1,
float x2, float z2,
float x3, float z3,
float cy_pos, float height)
{
sBarrier* bar = add_barrier(BARRIER_SPHERE, id, enabled, x_pos, y_pos, z_pos, x_rot, y_rot, z_rot);
bar->x1 = x1;
bar->z1 = z1;
bar->x2 = x2;
bar->z2 = z2;
bar->x3 = x3;
bar->z3 = z3;
bar->y1 = cy_pos;
bar->y2 = height;
}
void remove(long id)
{
sBarrier* bar = m_root_barrier;
while(bar != NULL)
{
sBarrier* next_bar = bar->next;
if(bar->id == id)
{
// remove from list
if(bar->prev)
bar->prev->next = bar->next;
else
m_root_barrier = bar->next;
if(bar->next)
bar->next->prev = bar->prev;
bar->next = NULL;
delete bar;
m_num_barriers--;
}
bar = next_bar;
}
}
bool get_enable_state(long id)
{
for(sBarrier* bar = m_root_barrier; bar != NULL; bar = bar->next)
{
if(bar->id == id)
return bar->enabled;
}
return false;
}
void enable(long id)
{
set_enable_state(id, true);
}
void disable(long id)
{
set_enable_state(id, false);
}
//////////////////////////////////////////////////////////////////////
public:
bool load(const char* filename);
bool save(const char* filename);
void render(ulong time_now, cFrustum* frustum);
long get_barrier(float x_pos, float y_pos, float z_pos);
} *cBarrierPtr;
Other function:
bool cBarrier::load(const char* filename)
{
free(); // remove all current barriers
FILE* fp = fopen(filename, "rb");
if(fp == NULL)
return false;
// start looping, reading in until EOF reached.
for(;;)
{
long id = get_next_long_2(fp);
if(id == -1)
break;
long type = get_next_long_2(fp);
bool enabled = get_next_long_2(fp) ? true : false;
// get coordinate and rotation
float x_pos = get_next_float_2(fp);
float y_pos = get_next_float_2(fp);
float z_pos = get_next_float_2(fp);
float x_rot = get_next_float_2(fp);
float y_rot = get_next_float_2(fp);
float z_rot = get_next_float_2(fp);
float x1, y1, z1, x2, y2, z2, x3, z3, radius;
// read in rest depending on type
switch(type)
{
case BARRIER_SPHERE:
x1 = get_next_float_2(fp);
y1 = get_next_float_2(fp);
z1 = get_next_float_2(fp);
radius = get_next_float_2(fp);
add_sphere(id, enabled,
x_pos, y_pos, z_pos, x_rot, y_rot, z_rot,
x1, y1, z1, radius);
break;
case BARRIER_BOX:
x1 = get_next_float_2(fp);
y1 = get_next_float_2(fp);
z1 = get_next_float_2(fp);
x2 = get_next_float_2(fp);
y2 = get_next_float_2(fp);
z2 = get_next_float_2(fp);
add_box(id, enabled,
x_pos, y_pos, z_pos, x_rot, y_rot, z_rot,
x1, y1, z1, x2, y2, z2);
break;
case BARRIER_CYLINDER:
x1 = get_next_float_2(fp);
y1 = get_next_float_2(fp);
z1 = get_next_float_2(fp);
radius = get_next_float_2(fp);
y2 = get_next_float_2(fp);
add_cylinder(id, enabled,
x_pos, y_pos, z_pos, x_rot, y_rot, z_rot,
x1, y1, z1, radius, y2);
break;
case BARRIER_TRIANGLE:
x1 = get_next_float_2(fp);
z1 = get_next_float_2(fp);
x2 = get_next_float_2(fp);
z2 = get_next_float_2(fp);
x3 = get_next_float_2(fp);
z3 = get_next_float_2(fp);
y1 = get_next_float_2(fp);
y2 = get_next_float_2(fp);
add_triangle(id, enabled,
x_pos, y_pos, z_pos, x_rot, y_rot, z_rot,
x1, z1, x2, z2, x3, z3, y1, y2);
break;
default: // some error occurred
fclose(fp);
free();
return false;
}
}
fclose(fp);
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////
bool cBarrier::save(const char* filename)
{
if(m_num_barriers == 0)
return false;
FILE* fp = fopen(filename, "wb");
if(fp == NULL)
return false;
for(sBarrier* bar = m_root_barrier; bar != NULL; bar = bar->next)
{
int enabled = bar->enabled ? 1 : 0;
char buf[1024];
sprintf(buf, "%lu %lu %lu %lf %lf %lf %lf %lf %lf",
bar->id, bar->type, enabled,
bar->x_pos, bar->y_pos, bar->z_pos,
bar->x_rot, bar->y_rot, bar->z_rot);
// write out remaining data depending on type
switch(bar->type)
{
case BARRIER_SPHERE:
fprintf(fp, "%s %lf %lf %lf %lf\r\n",
buf, bar->x1, bar->y1, bar->z1, bar->radius);
break;
case BARRIER_BOX:
fprintf(fp, "%s %lf %lf %lf %lf %lf %lf\r\n",
buf, bar->x1, bar->y1, bar->z1, bar->x2, bar->y2, bar->z2);
break;
case BARRIER_CYLINDER:
fprintf(fp, "%s %lf %lf %lf %lf %lf\r\n",
buf, bar->x1, bar->y1, bar->z1, bar->radius, bar->y2);
break;
case BARRIER_TRIANGLE:
fprintf(fp, "%s %lf %lf %lf %lf %lf %lf %lf %lf\r\n",
buf,
bar->x1, bar->z1, bar->x2, bar->z2, bar->x3, bar->z3,
bar->y1, bar->y2);
break;
}
}
fclose(fp);
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////
long cBarrier::get_barrier(float x_pos, float y_pos, float z_pos)
{
D3DXVECTOR2 norm_vec;
for(sBarrier* bar = m_root_barrier; bar != NULL; bar = bar->next)
{
// only bother if enabled
if(! bar->enabled)
continue;
float x_diff, y_diff, z_diff, dist;
switch(bar->type)
{
case BARRIER_SPHERE:
// check distance from sphere
x_diff = fabs(bar->x1 - x_pos);
y_diff = fabs(bar->y1 - y_pos);
z_diff = fabs(bar->z1 - z_pos);
dist = x_diff * x_diff + y_diff * y_diff + z_diff * z_diff;
if(dist <= bar->radius)
return bar->id;
break;
case BARRIER_BOX:
// check if inside box
if((x_pos >= bar->x1 && x_pos <= bar->x2) &&
(y_pos >= bar->y1 && y_pos <= bar->y2) &&
(z_pos >= bar->z1 && z_pos <= bar->z2))
{
return bar->id;
}
break;
case BARRIER_CYLINDER:
// first make sure within height bounds
if(y_pos >= bar->y1 && y_pos <= bar->y1 + bar->y2)
{
// check distance from cylinder
x_diff = fabs(bar->x1 - x_pos);
y_diff = fabs(bar->y1 - y_pos);
z_diff = fabs(bar->z1 - z_pos);
dist = x_diff * x_diff + y_diff * y_diff + z_diff * z_diff;
if(dist <= bar->radius)
return bar->id;
}
break;
case BARRIER_TRIANGLE:
// first make sure within height bounds
if(y_pos >= bar->y1 && y_pos <= bar->y1 + bar->y2)
{
// check if point in front of all lines
// x1,z1 to x2,z2
D3DXVec2Normalize(&norm_vec, &D3DXVECTOR2(bar->z2 - bar->z1, bar->x1 - bar->x2));
if(D3DXVec2Dot(&D3DXVECTOR2(x_pos - bar->x1, z_pos - bar->z1), &norm_vec) < 0)
break;
// x2,z2 to x3,z3
D3DXVec2Normalize(&norm_vec, &D3DXVECTOR2(bar->z3 - bar->z2, bar->x2 - bar->x3));
if(D3DXVec2Dot(&D3DXVECTOR2(x_pos - bar->x2, z_pos - bar->z2), &norm_vec) < 0)
break;
// x3,z3 to x1,z1
D3DXVec2Normalize(&norm_vec, &D3DXVECTOR2(bar->z1 - bar->z3, bar->x3 - bar->x1));
if(D3DXVec2Dot(&D3DXVECTOR2(x_pos - bar->x3, z_pos - bar->z3), &norm_vec) < 0)
break;
return bar->id;
}
break;
}
}
return 0; // means no barrier found
}