Files
fs2open.github.com/code/model/modeloctant.cpp
T
asarium abdef9f7b0 Add tracing scopes to the mission load code
This makes it easier to determine what takes the most time while loading
the mission (loading models) and how we could optimize that load time
(optimize model loading).

This is not a bug fix but it also shouldn't cause any regressions since
there are no actual code changes so this could be merged before the
release of 3.8.
2017-03-28 17:23:22 +02:00

504 lines
11 KiB
C++

/*
* Copyright (C) Volition, Inc. 1999. All rights reserved.
*
* All source code herein is the property of Volition, Inc. You may not sell
* or otherwise commercially exploit the source or things you created based on the
* source.
*
*/
#include <math.h>
#define MODEL_LIB
#include "cmdline/cmdline.h"
#include "math/vecmat.h"
#include "model/model.h"
#include "model/modelsinc.h"
#include "tracing/tracing.h"
// returns 1 if a point is in an octant.
int point_in_octant( polymodel * pm, model_octant * oct, vec3d *vert )
{
if ( vert->xyz.x < oct->min.xyz.x ) return 0;
if ( vert->xyz.x > oct->max.xyz.x ) return 0;
if ( vert->xyz.y < oct->min.xyz.y ) return 0;
if ( vert->xyz.y > oct->max.xyz.y ) return 0;
if ( vert->xyz.z < oct->min.xyz.z) return 0;
if ( vert->xyz.z > oct->max.xyz.z) return 0;
return 1;
}
void model_octant_find_shields( polymodel * pm, model_octant * oct )
{
int i, j, n;
shield_tri *tri;
n = 0;
// Scan all the triangles in the mesh to find how many tris there are.
for (i=0; i<pm->shield.ntris; i++ ) {
tri = &pm->shield.tris[i];
for (j=0; j<3; j++ ) {
if ( point_in_octant( pm, oct, &pm->shield.verts[tri->verts[j]].pos )) {
n++;
break;
}
}
}
//mprintf(( "Octant has %d shield polys in it\n", n ));
oct->nshield_tris = n;
// if we don't have any shield polys then don't bother continuing - taylor
if (oct->nshield_tris <= 0) {
oct->shield_tris = NULL;
return;
}
oct->shield_tris = (shield_tri **)vm_malloc( sizeof(shield_tri *) * oct->nshield_tris );
Assert(oct->shield_tris!=NULL);
n = 0;
// Rescan all the triangles in the mesh.
for (i=0; i<pm->shield.ntris; i++ ) {
tri = &pm->shield.tris[i];
for (j=0; j<3; j++ ) {
if ( point_in_octant( pm, oct, &pm->shield.verts[tri->verts[j]].pos )) {
oct->shield_tris[n++] = tri;
break;
}
}
}
Assert( oct->nshield_tris == n );
}
void moff_defpoints(ubyte * p, int just_count)
{
int n;
int nverts = w(p+8);
int offset = w(p+16);
int nnorms = 0;
// if we are just counting then we don't need to be here
if (just_count)
return;
ubyte * normcount = p+20;
vec3d *src = vp(p+offset);
// make sure we have enough space allocated for the new data
for (n = 0; n < nverts; n++) {
nnorms += normcount[n];
}
model_allocate_interp_data( nverts, nnorms );
Assert( Interp_verts != NULL );
for (n=0; n<nverts; n++ ) {
Interp_verts[n] = src;
src += normcount[n]+1;
}
}
// Textured Poly
// +0 int id
// +4 int size
// +8 vec3d normal
// +20 vec3d center
// +32 float radius
// +36 int nverts
// +40 int tmap_num
// +44 nverts*(model_tmap_vert) vertlist (n,u,v)
void moff_tmappoly(ubyte * p, polymodel * pm, model_octant * oct, int just_count )
{
int i, nv;
model_tmap_vert *verts;
nv = w(p+36);
if ( nv < 0 ) return;
verts = (model_tmap_vert *)(p+44);
if ( (pm->version < 2003) && !just_count ) {
// Set the "normal_point" part of field to be the center of the polygon
vec3d center_point;
vm_vec_zero( &center_point );
Assert( Interp_verts != NULL );
for (i=0;i<nv;i++) {
vm_vec_add2( &center_point, Interp_verts[verts[i].vertnum] );
}
center_point.xyz.x /= nv;
center_point.xyz.y /= nv;
center_point.xyz.z /= nv;
*vp(p+20) = center_point;
float rad = 0.0f;
for (i=0;i<nv;i++) {
float dist = vm_vec_dist( &center_point, Interp_verts[verts[i].vertnum] );
if ( dist > rad ) {
rad = dist;
}
}
fl(p+32) = rad;
}
// Put each face into a particular octant
if ( point_in_octant( pm, oct, vp(p+20) ) ) {
if (just_count)
oct->nverts++;
else
oct->verts[oct->nverts++] = vp(p+20);
return;
}
}
// Flat Poly
// +0 int id
// +4 int size
// +8 vec3d normal
// +20 vec3d center
// +32 float radius
// +36 int nverts
// +40 byte red
// +41 byte green
// +42 byte blue
// +43 byte pad
// +44 nverts*int vertlist
void moff_flatpoly(ubyte * p, polymodel * pm, model_octant * oct, int just_count )
{
int i, nv;
short *verts;
nv = w(p+36);
if ( nv < 0 ) return;
verts = (short *)(p+44);
if ( (pm->version < 2003) && !just_count ) {
// Set the "normal_point" part of field to be the center of the polygon
vec3d center_point;
vm_vec_zero( &center_point );
Assert( Interp_verts != NULL );
for (i=0;i<nv;i++) {
vm_vec_add2( &center_point, Interp_verts[verts[i*2]] );
}
center_point.xyz.x /= nv;
center_point.xyz.y /= nv;
center_point.xyz.z /= nv;
*vp(p+20) = center_point;
float rad = 0.0f;
for (i=0;i<nv;i++) {
float dist = vm_vec_dist( &center_point, Interp_verts[verts[i*2]] );
if ( dist > rad ) {
rad = dist;
}
}
fl(p+32) = rad;
}
// Put each face's center point into a particular octant
if ( point_in_octant( pm, oct, vp(p+20) ) ) {
if (just_count)
oct->nverts++;
else
oct->verts[oct->nverts++] = vp(p+20);
}
}
int model_octant_find_faces_sub(polymodel * pm, model_octant * oct, void *model_ptr, int just_count )
{
ubyte *p = (ubyte *)model_ptr;
int chunk_type, chunk_size;
chunk_type = w(p);
chunk_size = w(p+4);
while (chunk_type != OP_EOF) {
switch (chunk_type) {
case OP_DEFPOINTS:
moff_defpoints(p, just_count);
break;
case OP_FLATPOLY: moff_flatpoly(p, pm, oct, just_count ); break;
case OP_TMAPPOLY: moff_tmappoly(p, pm, oct, just_count ); break;
case OP_SORTNORM: {
int frontlist = w(p+36);
int backlist = w(p+40);
int prelist = w(p+44);
int postlist = w(p+48);
int onlist = w(p+52);
if (prelist) model_octant_find_faces_sub(pm,oct,p+prelist,just_count);
if (backlist) model_octant_find_faces_sub(pm,oct,p+backlist,just_count);
if (onlist) model_octant_find_faces_sub(pm,oct,p+onlist,just_count);
if (frontlist) model_octant_find_faces_sub(pm,oct,p+frontlist,just_count);
if (postlist) model_octant_find_faces_sub(pm,oct,p+postlist,just_count);
}
break;
case OP_BOUNDBOX: break;
default:
mprintf(( "Bad chunk type %d, len=%d in model_octant_find_faces_sub\n", chunk_type, chunk_size ));
Int3(); // Bad chunk type!
return 0;
}
p += chunk_size;
chunk_type = w(p);
chunk_size = w(p+4);
}
return 1;
}
void model_octant_find_faces( polymodel * pm, model_octant * oct )
{
ubyte *p;
int submodel_num = pm->detail[0];
p = pm->submodel[submodel_num].bsp_data;
oct->nverts = 0;
model_octant_find_faces_sub(pm, oct, p, 1 );
if ( oct->nverts < 1 ) {
oct->nverts = 0;
oct->verts = NULL;
return;
}
oct->verts = (vec3d **)vm_malloc( sizeof(vec3d *) * oct->nverts );
Assert(oct->verts!=NULL);
oct->nverts = 0;
model_octant_find_faces_sub(pm, oct, p, 0 );
// mprintf(( "Octant has %d faces\n", oct->nfaces ));
}
// Creates the octants for a given polygon model
void model_octant_create( polymodel * pm )
{
TRACE_SCOPE(tracing::ModelCreateOctants);
vec3d min, max, center;
int i, x, y, z;
min = pm->mins;
max = pm->maxs;
vm_vec_avg( &center, &min, &max );
for (i=0; i<8; i++ ) {
x = i & 4;
y = i & 2;
z = i & 1;
if ( x ) {
pm->octants[i].max.xyz.x = max.xyz.x;
pm->octants[i].min.xyz.x = center.xyz.x;
} else {
pm->octants[i].max.xyz.x = center.xyz.x;
pm->octants[i].min.xyz.x = min.xyz.x;
}
if ( y ) {
pm->octants[i].max.xyz.y = max.xyz.y;
pm->octants[i].min.xyz.y = center.xyz.y;
} else {
pm->octants[i].max.xyz.y = center.xyz.y;
pm->octants[i].min.xyz.y = min.xyz.y;
}
if ( z ) {
pm->octants[i].max.xyz.z = max.xyz.z;
pm->octants[i].min.xyz.z = center.xyz.z;
} else {
pm->octants[i].max.xyz.z = center.xyz.z;
pm->octants[i].min.xyz.z = min.xyz.z;
}
model_octant_find_shields( pm, &pm->octants[i] );
model_octant_find_faces( pm, &pm->octants[i] );
}
}
// frees the memory the octants use for a given polygon model
void model_octant_free( polymodel * pm )
{
int i;
for (i=0; i<8; i++ ) {
model_octant * oct = &pm->octants[i];
if ( oct->verts ) {
vm_free(oct->verts);
oct->verts = NULL;
}
if ( oct->shield_tris ) {
vm_free( oct->shield_tris );
oct->shield_tris = NULL;
}
}
}
// Returns which octant point pnt is closet to. This will always return
// a valid octant (0-7) since the point doesn't have to be in an octant.
// If model_orient and/or model_pos are NULL, pnt is assumed to already
// be rotated into the model's local coordinates.
// If oct is not null, it will be filled in with a pointer to the octant
// data.
int model_which_octant_distant_many( vec3d *pnt, int model_num,matrix *model_orient, vec3d * model_pos, polymodel **pm, int *octs)
{
vec3d tempv, rotpnt;
*pm = model_get(model_num);
if ( model_orient && model_pos ) {
// First, rotate pnt into the model's frame of reference.
vm_vec_sub( &tempv, pnt, model_pos );
vm_vec_rotate( &rotpnt, &tempv, model_orient );
} else {
rotpnt = *pnt;
}
vec3d center;
vm_vec_avg( &center, &((*pm)->mins), &((*pm)->maxs ));
int i, x, y, z;
if ( rotpnt.xyz.x > center.xyz.x ) x = 1; else x = 0;
if ( rotpnt.xyz.y > center.xyz.y ) y = 1; else y = 0;
if ( rotpnt.xyz.z > center.xyz.z ) z = 1; else z = 0;
i = ( (x<<2) | (y<<1) | z );
octs[0] = i;
octs[1] = i ^ 4; // Adjacent octant in x dimension
octs[2] = i ^ 2; // Adjacent octant in y dimension
octs[3] = i ^ 1; // Adjacent octant in z dimension
return i;
}
// Returns which octant point pnt is closet to. This will always return
// a valid octant (0-7) since the point doesn't have to be in an octant.
// If model_orient and/or model_pos are NULL, pnt is assumed to already
// be rotated into the model's local coordinates.
// If oct is not null, it will be filled in with a pointer to the octant
// data.
int model_which_octant_distant( vec3d *pnt, int model_num,matrix *model_orient, vec3d * model_pos, model_octant **oct )
{
polymodel * pm;
vec3d tempv, rotpnt;
pm = model_get(model_num);
if ( model_orient && model_pos ) {
// First, rotate pnt into the model's frame of reference.
vm_vec_sub( &tempv, pnt, model_pos );
vm_vec_rotate( &rotpnt, &tempv, model_orient );
} else {
rotpnt = *pnt;
}
vec3d center;
vm_vec_avg( &center, &pm->mins, &pm->maxs );
int i, x, y, z;
if ( rotpnt.xyz.x > center.xyz.x ) x = 1; else x = 0;
if ( rotpnt.xyz.y > center.xyz.y ) y = 1; else y = 0;
if ( rotpnt.xyz.z > center.xyz.z ) z = 1; else z = 0;
i = ( (x<<2) | (y<<1) | z );
if ( oct )
*oct = &pm->octants[i];
return i;
}
// Returns which octant point pnt is in. This might return
// -1 if the point isn't in any octant.
// If model_orient and/or model_pos are NULL, pnt is assumed to already
// be rotated into the model's local coordinates.
// If oct is not null, it will be filled in with a pointer to the octant
// data. Or NULL if the pnt isn't in the octant.
int model_which_octant( vec3d *pnt, int model_num,matrix *model_orient, vec3d * model_pos, model_octant **oct )
{
polymodel * pm;
vec3d tempv, rotpnt;
pm = model_get(model_num);
if ( model_orient && model_pos ) {
// First, rotate pnt into the model's frame of reference.
vm_vec_sub( &tempv, pnt, model_pos );
vm_vec_rotate( &rotpnt, &tempv, model_orient );
} else {
rotpnt = *pnt;
}
vec3d center;
vm_vec_avg( &center, &pm->mins, &pm->maxs );
int i, x, y, z;
if ( rotpnt.xyz.x > center.xyz.x ) x = 1; else x = 0;
if ( rotpnt.xyz.y > center.xyz.y ) y = 1; else y = 0;
if ( rotpnt.xyz.z > center.xyz.z ) z = 1; else z = 0;
i = (x<<2) | (y<<1) | z;
if ( point_in_octant( pm, &pm->octants[i], &rotpnt ) ) {
if ( oct )
*oct = &pm->octants[i];
return i;
}
if ( oct )
*oct = NULL;
return -1;
}