Paranoia2/utils/p2rad/facepos.cpp

577 lines
14 KiB
C++

/***
*
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
****/
#include "qrad.h"
#define MAX_NUDGES 12
static const vec3_t g_nudgelist[MAX_NUDGES] =
{
{ 0.1, 0.0, 0.0 }, {-0.1, 0.0, 0.0 }, { 0.0, 0.1, 0.0 },
{ 0.0,-0.1, 0.0 }, { 0.3, 0.0, 0.0 }, {-0.3, 0.0, 0.0 },
{ 0.0, 0.3, 0.0 }, { 0.0,-0.3, 0.0 }, { 0.3, 0.3, 0.0 },
{-0.3, 0.3, 0.0 }, {-0.3,-0.3, 0.0 }, { 0.3,-0.3, 0.0 }
};
typedef struct
{
bool valid;
vec_t best_s; // FindNearestPosition will return this value
vec_t best_t;
vec3_t pos; // with DEFAULT_HUNT_OFFSET
} position_t;
// Size of position_t (21) * positions per sample (9) * max number of samples (max AllocBlock (64) * 128 * 128)
// = 200MB of RAM
// But they are freed before BuildVisLeafs, so it's not a problem.
typedef struct
{
bool valid;
int facenum;
vec3_t face_offset;
matrix3x4 worldtotex;
matrix3x4 textoworld;
winding_t *facewinding;
dplane_t faceplane;
winding_t *facewindingwithoffset;
dplane_t faceplanewithoffset;
winding_t *texwinding;
dplane_t texplane; // (0, 0, 1, 0) or (0, 0, -1, 0)
vec3_t texcentroid;
vec3_t start; // s_start, t_start, 0
vec3_t step; // s_step, t_step, 0
int w; // number of s
int h; // number of t
position_t *grid; // [h][w]
} positionmap_t;
static positionmap_t g_face_positions[MAX_MAP_FACES];
static bool IsPositionValid( int threadnum, positionmap_t *map, const vec3_t pos_st, vec3_t pos_out, bool phong = true, bool edgetest = true )
{
vec3_t pos, pos_normal;
vec_t hunt_scale = 0.2;
int hunt_size = 2;
vec_t hunt_offset;
Matrix3x4_VectorTransform( map->textoworld, pos_st, pos );
VectorAdd( pos, map->face_offset, pos );
if( phong )
{
GetPhongNormal( map->facenum, pos, pos_normal );
}
else
{
VectorCopy( map->faceplanewithoffset.normal, pos_normal );
}
VectorMA( pos, DEFAULT_HUNT_OFFSET, pos_normal, pos );
// might be smaller than DEFAULT_HUNT_OFFSET
hunt_offset = DotProduct( pos, map->faceplanewithoffset.normal ) - map->faceplanewithoffset.dist;
// push the point 0.2 units around to avoid walls
if( !HuntForWorld( pos, vec3_origin, &map->faceplanewithoffset, hunt_size, hunt_scale, hunt_offset ))
{
return false;
}
if( edgetest && !PointInWindingEpsilon( map->facewindingwithoffset, map->faceplanewithoffset.normal, pos, DEFAULT_EDGE_WIDTH ))
{
vec_t width = DEFAULT_EDGE_WIDTH;
vec3_t test;
VectorCopy( pos, test );
// if the sample has gone beyond face boundaries, be careful that it hasn't passed a wall
WindingSnapPointEpsilon( map->facewindingwithoffset, map->faceplanewithoffset.normal, test, width, width * 4 );
if( !HuntForWorld( test, vec3_origin, &map->faceplanewithoffset, hunt_size, hunt_scale, hunt_offset ))
{
return false;
}
if( TestLine( threadnum, pos, test, true ) != CONTENTS_EMPTY )
return false;
}
VectorCopy( pos, pos_out );
return true;
}
static void CalcSinglePosition( int threadnum, positionmap_t *map, int is, int it )
{
vec_t smin, smax, tmin, tmax;
const vec3_t v_s = { 1.0, 0.0, 0.0 };
const vec3_t v_t = { 0.0, 1.0, 0.0 };
dplane_t clipplanes[4];
winding_t *zone;
position_t *p;
p = &map->grid[is + map->w * it];
smin = map->start[0] + is * map->step[0];
smax = map->start[0] + (is + 1) * map->step[0];
tmin = map->start[1] + it * map->step[1];
tmax = map->start[1] + (it + 1) * map->step[1];
// setup clip planes
VectorScale( v_s, 1.0, clipplanes[0].normal );
clipplanes[0].dist = smin;
VectorScale( v_s, -1.0, clipplanes[1].normal );
clipplanes[1].dist = -smax;
VectorScale( v_t, 1.0, clipplanes[2].normal );
clipplanes[2].dist = tmin;
VectorScale( v_t, -1.0, clipplanes[3].normal );
clipplanes[3].dist = -tmax;
zone = CopyWinding( map->texwinding );
for( int x = 0; x < 4 && zone != NULL; x++ )
ChopWindingInPlace( &zone, clipplanes[x].normal, clipplanes[x].dist, ON_EPSILON, false );
if( !zone )
{
p->valid = false;
}
else
{
vec3_t original_st;
vec3_t test_st;
original_st[0] = map->start[0] + (is + 0.5) * map->step[0];
original_st[1] = map->start[1] + (it + 0.5) * map->step[1];
original_st[2] = 0.0;
p->valid = false;
VectorCopy (original_st, test_st);
WindingSnapPoint( zone, map->texplane.normal, test_st );
if( IsPositionValid( threadnum, map, test_st, p->pos ))
{
p->best_s = test_st[0];
p->best_t = test_st[1];
p->valid = true;
}
if( !p->valid )
{
WindingCenter( zone, test_st );
if( IsPositionValid( threadnum, map, test_st, p->pos ))
{
p->best_s = test_st[0];
p->best_t = test_st[1];
p->valid = true;
}
}
if( !p->valid && !g_fastmode )
{
for( int i = 0; i < MAX_NUDGES; i++ )
{
VectorMultiply( g_nudgelist[i], map->step, test_st );
VectorAdd( test_st, original_st, test_st );
WindingSnapPoint( zone, map->texplane.normal, test_st );
if( IsPositionValid( threadnum, map, test_st, p->pos ))
{
p->best_s = test_st[0];
p->best_t = test_st[1];
p->valid = true;
break;
}
}
}
FreeWinding( zone );
}
}
/*
============
FindFacePositions
assume g_face_offset and g_face_centroids are valid
and g_edgeshare have been calculated
============
*/
void FindFacePositions( int facenum, int threadnum )
{
vec_t texmins[2], texmaxs[2];
int imins[2], imaxs[2];
const vec3_t v_up = { 0, 0, 1 };
int x, k, is, it;
vec_t density;
positionmap_t *map;
dtexinfo_t *ti;
dface_t *f;
vec3_t v;
f = &g_dfaces[facenum];
map = &g_face_positions[facenum];
map->valid = true;
map->facenum = facenum;
map->facewinding = NULL;
map->facewindingwithoffset = NULL;
map->texwinding = NULL;
map->grid = NULL;
ti = &g_texinfo[f->texinfo];
if( FBitSet( ti->flags, TEX_SPECIAL ))
{
map->valid = false;
return;
}
VectorCopy( g_face_offset[facenum], map->face_offset );
TranslateWorldToTex( facenum, map->worldtotex );
if( !Matrix3x4_Invert_Full( map->textoworld, map->worldtotex ))
{
map->valid = false;
return;
}
map->facewinding = WindingFromFace( f );
map->faceplane = *GetPlaneFromFace( f );
map->facewindingwithoffset = AllocWinding( map->facewinding->numpoints );
map->facewindingwithoffset->numpoints = map->facewinding->numpoints;
for( x = 0; x < map->facewinding->numpoints; x++ )
VectorAdd( map->facewinding->p[x], map->face_offset, map->facewindingwithoffset->p[x] );
map->faceplanewithoffset = map->faceplane;
map->faceplanewithoffset.dist = map->faceplane.dist + DotProduct( map->face_offset, map->faceplane.normal );
map->texwinding = AllocWinding( map->facewinding->numpoints );
map->texwinding->numpoints = map->facewinding->numpoints;
for( x = 0; x < map->facewinding->numpoints; x++ )
{
Matrix3x4_VectorTransform( map->worldtotex, map->facewinding->p[x], map->texwinding->p[x] );
map->texwinding->p[x][2] = 0.0;
}
RemoveColinearPointsEpsilon( map->texwinding, ON_EPSILON );
VectorCopy( v_up, map->texplane.normal );
if( Matrix3x4_CalcSign( map->worldtotex ) < 0.0 )
map->texplane.normal[2] *= -1.0;
map->texplane.dist = 0.0;
if( map->texwinding->numpoints == 0 )
{
FreeWinding( map->facewindingwithoffset );
FreeWinding( map->facewinding );
FreeWinding( map->texwinding );
map->facewindingwithoffset = NULL;
map->facewinding = NULL;
map->texwinding = NULL;
map->valid = false;
return;
}
VectorSubtract( g_face_centroids[facenum], map->face_offset, v );
Matrix3x4_VectorTransform( map->worldtotex, v, map->texcentroid );
map->texcentroid[2] = 0.0;
int texture_step = GetTextureStep( ti );
for( x = 0; x < map->texwinding->numpoints; x++ )
{
for( k = 0; k < 2; k++ )
{
if( x == 0 || map->texwinding->p[x][k] < texmins[k] )
texmins[k] = map->texwinding->p[x][k];
if( x == 0 || map->texwinding->p[x][k] > texmaxs[k] )
texmaxs[k] = map->texwinding->p[x][k];
}
}
if( g_fastmode || texture_step == 1.0 )
density = 1.0;
else if( texture_step < 8.0 )
density = 2.0;
else density = 3.0;
// experimental
density = 1.0f;
map->step[0] = (vec_t)texture_step / density;
map->step[1] = (vec_t)texture_step / density;
map->step[2] = 1.0;
for( k = 0; k < 2; k++ )
{
imins[k] = (int)floor( texmins[k] / map->step[k] + 0.5 - ON_EPSILON );
imaxs[k] = (int)ceil( texmaxs[k] / map->step[k] - 0.5 + ON_EPSILON );
}
map->start[0] = (imins[0] - 0.5) * map->step[0];
map->start[1] = (imins[1] - 0.5) * map->step[1];
map->start[2] = 0.0;
map->w = imaxs[0] - imins[0] + 1;
map->h = imaxs[1] - imins[1] + 1;
if( map->w <= 0 || map->h <= 0 || (double)map->w * (double)map->h > 99999999 )
{
FreeWinding( map->facewindingwithoffset );
FreeWinding( map->facewinding );
FreeWinding( map->texwinding );
map->facewindingwithoffset = NULL;
map->facewinding = NULL;
map->texwinding = NULL;
map->valid = false;
return;
}
map->grid = (position_t *)Mem_Alloc( map->w * map->h * sizeof( position_t ));
for( it = 0; it < map->h; it++ )
{
for( is = 0; is < map->w; is++ )
{
CalcSinglePosition( threadnum, map, is, it );
}
}
}
void FreeFacePositions( void )
{
if( g_drawsample )
{
char path[MAX_PATH], name[MAX_PATH];
Q_strncpy( path, source, sizeof( path ));
COM_StripExtension( path );
Q_snprintf( name, sizeof( name ), "%s.pts", path );
Msg( "Writing '%s' ...\n", name );
FILE *f = fopen( name, "w" );
if( f )
{
vec3_t v;
for( int i = 0; i < g_numfaces; i++ )
{
positionmap_t *map = &g_face_positions[i];
if( !map->valid ) continue;
for( int j = 0; j < map->h * map->w; j++ )
{
if( !map->grid[j].valid )
continue;
VectorCopy( map->grid[j].pos, v );
fprintf( f, "%g %g %g\n", v[0], v[1], v[2] );
}
}
fclose( f );
Msg( "OK.\n" );
}
else Msg( "Error.\n" );
}
for( int facenum = 0; facenum < g_numfaces; facenum++ )
{
positionmap_t *map = &g_face_positions[facenum];
if( map->valid )
{
FreeWinding( map->facewindingwithoffset );
FreeWinding( map->facewinding );
FreeWinding( map->texwinding );
Mem_Free( map->grid );
map->facewindingwithoffset = NULL;
map->facewinding = NULL;
map->texwinding = NULL;
map->valid = false;
map->grid = NULL;
}
}
}
bool FindNearestPosition( int facenum, const winding_t *w, vec_t s, vec_t t, vec3_t pos, vec_t *best_s, vec_t *best_t, vec_t *dist )
{
int itmin, itmax, ismin, ismax;
const vec3_t v_s = { 1, 0, 0 };
const vec3_t v_t = { 0, 1, 0 };
vec3_t original_st;
int x, is, it;
int best_is;
int best_it;
vec_t best_dist;
bool found;
positionmap_t *map;
vec3_t v;
map = &g_face_positions[facenum];
if( !map->valid ) return false;
VectorSet( original_st, s, t, 0.0 );
if( PointInWindingEpsilon( map->texwinding, map->texplane.normal, original_st, 4 * ON_EPSILON, false ))
{
itmin = (int)ceil(( original_st[1] - map->start[1] - 2 * ON_EPSILON ) / map->step[1]) - 1;
itmax = (int)floor(( original_st[1] - map->start[1] + 2 * ON_EPSILON ) / map->step[1]);
ismin = (int)ceil(( original_st[0] - map->start[0] - 2 * ON_EPSILON ) / map->step[0]) - 1;
ismax = (int)floor(( original_st[0] - map->start[0] + 2 * ON_EPSILON ) / map->step[0]);
itmin = Q_max( 0, itmin );
itmax = Q_min( itmax, map->h - 1 );
ismin = Q_max( 0, ismin );
ismax = Q_min( ismax, map->w - 1 );
found = false;
for( it = itmin; it <= itmax; it++ )
{
for( is = ismin; is <= ismax; is++ )
{
vec3_t current_st;
position_t *p;
vec_t d;
p = &map->grid[is + map->w * it];
if( !p->valid ) continue;
current_st[0] = p->best_s;
current_st[1] = p->best_t;
current_st[2] = 0.0;
VectorSubtract( current_st, original_st, v );
d = VectorLength( v );
if( !found || d < best_dist - 2 * ON_EPSILON )
{
found = true;
best_is = is;
best_it = it;
best_dist = d;
}
}
}
if( found )
{
position_t *p;
p = &map->grid[best_is + map->w * best_it];
VectorCopy( p->pos, pos );
*best_s = p->best_s;
*best_t = p->best_t;
*dist = 0.0;
return true;
}
}
itmin = map->h;
itmax = -1;
ismin = map->w;
ismax = -1;
for( x = 0; x < w->numpoints; x++ )
{
it = (int)floor(( w->p[x][1] - map->start[1] + 0.5 * ON_EPSILON ) / map->step[1]);
itmin = Q_min( itmin, it );
it = (int)ceil(( w->p[x][1] - map->start[1] - 0.5 * ON_EPSILON ) / map->step[1]) - 1;
itmax = Q_max( it, itmax );
is = (int)floor(( w->p[x][0] - map->start[0] + 0.5 * ON_EPSILON ) / map->step[0]);
ismin = Q_min( ismin, is );
is = (int)ceil(( w->p[x][0] - map->start[0] - 0.5 * ON_EPSILON ) / map->step[0]) - 1;
ismax = Q_max( is, ismax );
}
itmin = Q_max( 0, itmin );
itmax = Q_min( itmax, map->h - 1 );
ismin = Q_max( 0, ismin );
ismax = Q_min( ismax, map->w - 1 );
found = false;
for( it = itmin; it <= itmax; it++ )
{
for( is = ismin; is <= ismax; is++ )
{
vec3_t current_st;
position_t *p;
vec_t d;
p = &map->grid[is + map->w * it];
if( !p->valid ) continue;
current_st[0] = p->best_s;
current_st[1] = p->best_t;
current_st[2] = 0.0;
VectorSubtract( current_st, original_st, v );
d = VectorLength( v );
if( !found || d < best_dist - ON_EPSILON )
{
found = true;
best_is = is;
best_it = it;
best_dist = d;
}
}
}
if( found )
{
position_t *p;
p = &map->grid[best_is + map->w * best_it];
VectorCopy( p->pos, pos );
*best_s = p->best_s;
*best_t = p->best_t;
*dist = best_dist;
return true;
}
return false;
}
/*
============
CalcPositionsSize
============
*/
void CalcPositionsSize( void )
{
int total_size = 0;
for( int facenum = 0; facenum < g_numfaces; facenum++ )
{
positionmap_t *map = &g_face_positions[facenum];
if( !map->valid ) continue;
total_size += map->w * map->h * sizeof( position_t );
total_size += WindingSize( map->facewindingwithoffset );
total_size += WindingSize( map->facewinding );
total_size += WindingSize( map->texwinding );
}
MsgDev( D_INFO, "position maps: %s\n", Q_memprint( total_size ));
}