mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 04:49:39 -10:00
479 lines
11 KiB
C++
479 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 <cmath>
|
|
|
|
#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)
|
|
{
|
|
uint n;
|
|
uint nverts = uw(p+8);
|
|
uint offset = uw(p+16);
|
|
uint nnorms = 0;
|
|
|
|
// only do fill Interp_verts once, in the first pass when we're just counting them up
|
|
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_old) vertlist (n,u,v)
|
|
void moff_tmappoly(ubyte * p, polymodel * pm, model_octant * oct, int just_count )
|
|
{
|
|
uint i, nv;
|
|
|
|
nv = uw(p + TMAP_NVERTS);
|
|
|
|
auto verts = reinterpret_cast<model_tmap_vert_old*>(&p[TMAP_VERTS]);
|
|
|
|
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( ¢er_point );
|
|
|
|
Assert( Interp_verts != nullptr );
|
|
|
|
for (i=0;i<nv;i++) {
|
|
vm_vec_add2( ¢er_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( ¢er_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;
|
|
}
|
|
}
|
|
|
|
|
|
// Textured Poly version 2
|
|
// +0 int id = 6
|
|
// + 4 int size
|
|
// + 8 vector min_bounding_box_point
|
|
// + 20 vector max_bounding_box_point
|
|
// + 32 vector normal
|
|
// + 44 int tmap_num
|
|
// + 48 int nverts
|
|
// + 52 nverts*(model_tmap2_vert) vertlist (n,u,v)
|
|
void moff_tmap2poly(ubyte* p, polymodel* pm, model_octant* oct, int just_count)
|
|
{
|
|
Assert(pm->version >= 2300);
|
|
|
|
uint i, nv;
|
|
model_tmap_vert* verts;
|
|
|
|
nv = uw(p + TMAP2_NVERTS);
|
|
|
|
verts = reinterpret_cast<model_tmap_vert*>(p + TMAP2_VERTS);
|
|
|
|
vec3d center_point;
|
|
vm_vec_zero(¢er_point);
|
|
|
|
Assert(Interp_verts != nullptr);
|
|
|
|
for (i = 0; i < nv; i++) {
|
|
vm_vec_add2(¢er_point, Interp_verts[verts[i].vertnum]);
|
|
}
|
|
|
|
center_point.xyz.x /= nv;
|
|
center_point.xyz.y /= nv;
|
|
center_point.xyz.z /= nv;
|
|
|
|
// Put each face into a particular octant
|
|
if (point_in_octant(pm, oct, ¢er_point)) {
|
|
if (just_count)
|
|
oct->nverts++;
|
|
else
|
|
oct->verts[oct->nverts++] = ¢er_point;
|
|
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 )
|
|
{
|
|
uint i, nv;
|
|
short *verts;
|
|
|
|
nv = uw(p+36);
|
|
|
|
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( ¢er_point );
|
|
|
|
Assert( Interp_verts != NULL );
|
|
|
|
for (i=0;i<nv;i++) {
|
|
vm_vec_add2( ¢er_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( ¢er_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);
|
|
|
|
bool end = chunk_type == OP_EOF;
|
|
while (!end) {
|
|
|
|
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_SORTNORM2: {
|
|
int frontlist = w(p + 8);
|
|
int backlist = w(p + 12);
|
|
|
|
if (backlist) model_octant_find_faces_sub(pm, oct, p + backlist, just_count);
|
|
if (frontlist) model_octant_find_faces_sub(pm, oct, p + frontlist, just_count);
|
|
end = true; // should not continue after this chunk
|
|
}
|
|
break;
|
|
case OP_BOUNDBOX: break;
|
|
case OP_TMAP2POLY:
|
|
moff_tmap2poly(p, pm, oct, just_count);
|
|
end = true; // should not continue after this chunk
|
|
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);
|
|
|
|
if (chunk_type == OP_EOF)
|
|
end = true;
|
|
}
|
|
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( ¢er, &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( ¢er, &((*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;
|
|
}
|