* bobToolz::Vis Viewer add sortable leafs chart (visible leafs, surfaces, shaders)

add rendering of MST_PATCH, MST_TRIANGLE_SOUP
		use consistent color per colorable object
		option to assing color per surface, not leaf (to debug surfaces)(current leaf surfs are always green anyway)
		render current leaf convex volume, in white
		support bsp without vis data
		add RBSP, FBSP support
This commit is contained in:
Garux
2025-03-28 06:36:58 +05:00
parent e1cd441c10
commit 251d8ebd97
12 changed files with 646 additions and 249 deletions
+15 -14
View File
@@ -142,18 +142,18 @@ bool DPlane::operator !=( DPlane& other ){
return true;
}
DWinding* DPlane::BaseWindingForPlane(){
DWinding DPlane::BaseWindingForPlane(){
int i, x;
vec_t max, v;
vec3_t org, vright, vup;
// find the major axis
// find the major axis
max = -131072;
x = -1;
for ( i = 0; i < 3; i++ )
{
v = (float)fabs( normal[i] );
v = fabs( normal[i] );
if ( v > max ) {
x = i;
max = v;
@@ -161,6 +161,7 @@ DWinding* DPlane::BaseWindingForPlane(){
}
if ( x == -1 ) {
globalWarningStream() << "BaseWindingForPlane: no axis found";
return DWinding();
}
VectorCopy( vec3_origin, vup );
@@ -186,21 +187,21 @@ DWinding* DPlane::BaseWindingForPlane(){
VectorScale( vup, 131072, vup );
VectorScale( vright, 131072, vright );
// project a really big axis aligned box onto the plane
DWinding* w = new DWinding;
w->AllocWinding( 4 );
// project a really big axis aligned box onto the plane
DWinding w;
w.AllocWinding( 4 );
VectorSubtract( org, vright, w->p[0] );
VectorAdd( w->p[0], vup, w->p[0] );
VectorSubtract( org, vright, w.p[0] );
VectorAdd( w.p[0], vup, w.p[0] );
VectorAdd( org, vright, w->p[1] );
VectorAdd( w->p[1], vup, w->p[1] );
VectorAdd( org, vright, w.p[1] );
VectorAdd( w.p[1], vup, w.p[1] );
VectorAdd( org, vright, w->p[2] );
VectorSubtract( w->p[2], vup, w->p[2] );
VectorAdd( org, vright, w.p[2] );
VectorSubtract( w.p[2], vup, w.p[2] );
VectorSubtract( org, vright, w->p[3] );
VectorSubtract( w->p[3], vup, w->p[3] );
VectorSubtract( org, vright, w.p[3] );
VectorSubtract( w.p[3], vup, w.p[3] );
return w;
}
+3 -9
View File
@@ -27,20 +27,16 @@
#include "ibrush.h"
#include "string/string.h"
#include "mathlib.h"
class Brush;
class DPoint;
#include "DWinding.h"
#define FACE_DETAIL 0x8000000
class DWinding;
class DPlane
{
public:
DPlane( const vec3_t va, const vec3_t vb, const vec3_t vc, const char* textureName, bool bDetail );
void ScaleTexture();
DWinding* BaseWindingForPlane();
DWinding BaseWindingForPlane();
void Rebuild();
@@ -48,7 +44,7 @@ public:
bool operator !=( DPlane& other );
bool operator ==( const DPlane& other ) const;
bool IsRedundant( std::list<DPoint*>& pointList );
bool IsRedundant( std::list<class DPoint*>& pointList );
bool PlaneIntersection( DPlane* pl1, DPlane* pl2, vec3_t out );;
vec_t DistanceToPoint( vec3_t pnt );
@@ -64,5 +60,3 @@ public:
vec3_t normal;
float _d;
};
//typedef CList<DPlane*, DPlane*> DPlaneList;
+126 -15
View File
@@ -30,13 +30,28 @@
#include "misc.h"
#include "funchandlers.h"
#include "bsploader.h"
#include "bobToolz-GTK.h"
#include <QDialog>
#include <QCloseEvent>
#include <QVBoxLayout>
#include <QTableWidget>
#include <QHeaderView>
#include <QPushButton>
#include <QCheckBox>
#include "visfind.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
DVisDrawer::DVisDrawer(){
m_list = NULL;
m_list = nullptr;
m_colorPerSurf = false;
ui_create();
constructShaders();
GlobalShaderCache().attachRenderable( *this );
@@ -46,7 +61,11 @@ DVisDrawer::~DVisDrawer(){
GlobalShaderCache().detachRenderable( *this );
destroyShaders();
ClearPoints();
delete m_list;
delete m_dialog;
FreeBSPData();
SceneChangeNotify();
}
//////////////////////////////////////////////////////////////////////
@@ -83,11 +102,15 @@ void DVisDrawer::destroyShaders(){
void DVisDrawer::render( RenderStateFlags state ) const {
gl().glEnable( GL_POLYGON_OFFSET_FILL );
for( const auto surf : *m_list ){
const DMetaSurf& s = *surf;
gl().glColor4f( s.colour[0], s.colour[1], s.colour[2], 0.5f );
gl().glVertexPointer( 3, GL_FLOAT, sizeof( vec3_t ), s.verts );
gl().glDrawElements( GL_TRIANGLES, GLsizei( s.indicesN ), GL_UNSIGNED_INT, s.indices );
for( const DMetaSurf& surf : *m_list ){
gl().glColor4f( surf.colour[0], surf.colour[1], surf.colour[2], 0.5f );
gl().glVertexPointer( 3, GL_FLOAT, sizeof( vec3_t ), surf.verts );
gl().glDrawElements( GL_TRIANGLES, GLsizei( surf.indicesN ), GL_UNSIGNED_INT, surf.indices );
}
for( const DWinding& w : m_windings ){
gl().glColor4f( 1, 1, 1, 0.5f );
gl().glVertexPointer( 3, GL_FLOAT, sizeof( *w.p ), w.p );
gl().glDrawArrays( GL_POLYGON, 0, GLsizei( w.numpoints ) );
}
gl().glDisable( GL_POLYGON_OFFSET_FILL );
gl().glColor4f( 1, 1, 1, 1 );
@@ -115,16 +138,104 @@ void DVisDrawer::renderSolid( Renderer& renderer, const VolumeTest& volume ) con
}
void DVisDrawer::SetList( DMetaSurfaces* pointList ){
ClearPoints();
m_list = pointList;
delete std::exchange( m_list, pointList );
SceneChangeNotify();
}
void DVisDrawer::ClearPoints(){
if ( m_list ) {
for ( auto deadPoint : *m_list )
delete deadPoint;
m_list->clear();
delete m_list;
m_list = 0;
SetList( nullptr );
m_windings.clear();
m_table->setCurrentCell( -1, -1 ); // unset current item, so QTableWidget::currentCellChanged will be triggered for the same valid index later
}
class VisDialog : public QDialog
{
protected:
void closeEvent( QCloseEvent *event ) override {
event->ignore();
g_VisView.reset();
}
using QDialog::QDialog;
// excess, intent is to have arrows working in the table
bool event( QEvent *event ) override {
if( event->type() == QEvent::ShortcutOverride ){
event->accept();
return true;
}
return QDialog::event( event );
}
};
void DVisDrawer::ui_create(){
m_dialog = new VisDialog( g_pRadiantWnd, Qt::Dialog | Qt::WindowCloseButtonHint );
m_dialog->setWindowTitle( "BSP leafs" );
{
auto vbox = new QVBoxLayout( m_dialog );
{
m_table = new QTableWidget( 0, 4 );
vbox->addWidget( m_table );
m_table->setHorizontalHeaderLabels( { "leaf #", "leafs visible", "surfs visible", "shaders visible" } );
m_table->horizontalHeader()->setSortIndicator( 2, Qt::SortOrder::DescendingOrder ); // sort by nsurfs
m_table->setSizeAdjustPolicy( QAbstractScrollArea::SizeAdjustPolicy::AdjustToContentsOnFirstShow );
m_table->setHorizontalScrollBarPolicy( Qt::ScrollBarPolicy::ScrollBarAlwaysOff );
QObject::connect( m_table, &QTableWidget::currentCellChanged, [this]( int currentRow, int currentColumn, int previousRow, int previousColumn ){
// globalOutputStream() << previousRow << "->" << currentRow << ";" << previousColumn << "->" << currentColumn << "\n";
if( currentRow != previousRow && currentRow >= 0 ){
const int leafnum = m_table->item( currentRow, 0 )->data( Qt::ItemDataRole::DisplayRole ).toInt();
SetList( BuildTrace( leafnum, m_colorPerSurf ) );
m_windings = BuildLeafWindings( leafnum );
}
} );
m_table->setSelectionBehavior( QAbstractItemView::SelectionBehavior::SelectRows );
m_table->setSelectionMode( QAbstractItemView::SelectionMode::SingleSelection );
}
{
auto *check = new QCheckBox( "Color per leaf" );
vbox->addWidget( check );
QObject::connect( check, &QCheckBox::stateChanged, [check, this]( int on ){
if( on )
check->setText( "Color per surface" );
else
check->setText( "Color per leaf" );
m_colorPerSurf = on;
const int row = m_table->currentRow();
ClearPoints();
m_table->setCurrentCell( row, 0 );
} );
}
{
auto *butt = new QPushButton( "From Cam or Selection" );
vbox->addWidget( butt );
QObject::connect( butt, &QPushButton::clicked, []( bool ){ DoVisAnalyse(); } );
}
}
}
void DVisDrawer::ui_leaf_add( int leafnum, int nleafs, int nsurfs, int nshaders ){
const int row = m_table->rowCount();
m_table->insertRow( row );
auto setColData = [&]( int col, int num ){
auto *item = new QTableWidgetItem();
item->setFlags( Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable );
item->setData( Qt::ItemDataRole::DisplayRole, num );
m_table->setItem( row, col, item );
};
setColData( 0, leafnum );
setColData( 1, nleafs );
setColData( 2, nsurfs );
setColData( 3, nshaders );
}
void DVisDrawer::ui_show(){
m_table->setSortingEnabled( true ); // enable once after construction for performance
m_dialog->show();
}
void DVisDrawer::ui_leaf_show( int leafnum ){
for( int i = 0; i < m_table->rowCount(); ++i )
if( m_table->item( i, 0 )->data( Qt::ItemDataRole::DisplayRole ).toInt() == leafnum )
return m_table->setCurrentCell( i, 0 );
}
+29 -17
View File
@@ -23,7 +23,8 @@
#pragma once
#include <list>
#include <vector>
#include <utility>
#include "renderable.h"
#include "irender.h"
#include "mathlib.h"
@@ -31,12 +32,17 @@
class DMetaSurf
{
public:
DMetaSurf() = delete;
DMetaSurf( int numverts, int numindices ){
verts = new vec3_t[numverts];
indices = new unsigned int[numindices];
indicesN = numindices;
}
DMetaSurf( int numverts, int numindices )
: verts( new vec3_t[numverts] ),
indices( new unsigned int[numindices] ),
indicesN( numindices )
{}
DMetaSurf( DMetaSurf&& other ) noexcept
: verts( std::exchange( other.verts, nullptr ) ),
indices( std::exchange( other.indices, nullptr ) ),
indicesN( other.indicesN ),
colour{ other.colour[0], other.colour[1], other.colour[2] }
{}
~DMetaSurf(){
delete[] verts;
delete[] indices;
@@ -47,28 +53,34 @@ public:
vec3_t colour;
};
typedef std::list<DMetaSurf*> DMetaSurfaces;
using DMetaSurfaces = std::vector<DMetaSurf>;
class DVisDrawer : public Renderable, public OpenGLRenderable
{
Shader* m_shader_solid;
Shader* m_shader_wireframe;
class QDialog *m_dialog;
class QTableWidget *m_table;
DMetaSurfaces* m_list;
std::vector<class DWinding> m_windings;
bool m_colorPerSurf;
public:
DVisDrawer();
virtual ~DVisDrawer();
protected:
DMetaSurfaces* m_list;
int refCount;
public:
void ClearPoints();
private:
void SetList( DMetaSurfaces* pointList );
void ui_create();
public:
void ui_leaf_add( int leafnum, int nleafs, int nsurfs, int nshaders );
void ui_show();
void ui_leaf_show( int leafnum );
void render( RenderStateFlags state ) const;
void renderSolid( Renderer& renderer, const VolumeTest& volume ) const;
void renderWireframe( Renderer& renderer, const VolumeTest& volume ) const;
void render( RenderStateFlags state ) const override;
void renderSolid( Renderer& renderer, const VolumeTest& volume ) const override;
void renderWireframe( Renderer& renderer, const VolumeTest& volume ) const override;
private:
void constructShaders();
void destroyShaders();
};
+52 -46
View File
@@ -30,15 +30,24 @@
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
DWinding::DWinding(){
numpoints = 0;
p = NULL;
DWinding::DWinding()
: numpoints( 0 ),
p( nullptr )
{}
DWinding::DWinding( DWinding &&other ) noexcept
: numpoints( std::exchange( other.numpoints, 0 ) ),
p( std::exchange( other.p, nullptr ) )
{}
DWinding &DWinding::operator=( DWinding &&other ) noexcept {
std::swap( p, other.p );
std::swap( numpoints, other.numpoints );
return *this;
}
DWinding::~DWinding(){
if ( p ) {
delete[] p;
}
delete[] p;
}
//////////////////////////////////////////////////////////////////////
@@ -256,24 +265,23 @@ DWinding* DWinding::ReverseWinding(){
bool DWinding::ChopWindingInPlace( DPlane* chopPlane, vec_t epsilon ){
vec_t dists[MAX_POINTS_ON_WINDING + 4];
int sides[MAX_POINTS_ON_WINDING + 4];
int counts[3];
vec_t *p1, *p2;
vec3_t mid;
int counts[3] = {0};
counts[0] = counts[1] = counts[2] = 0;
if ( numpoints > MAX_POINTS_ON_WINDING ) {
globalWarningStream() << "ChopWindingInPlace: MAX_POINTS_ON_WINDING\n";
*this = DWinding();
return false;
}
// determine sides for each point
int i;
for ( i = 0; i < numpoints; i++ )
// determine sides for each point
for ( int i = 0; i < numpoints; i++ )
{
vec_t dot = DotProduct( p[i], chopPlane->normal );
dot -= chopPlane->_d;
dists[i] = dot;
dists[i] = DotProduct( p[i], chopPlane->normal ) - chopPlane->_d;
if ( dot > epsilon ) {
if ( dists[i] > epsilon ) {
sides[i] = SIDE_FRONT;
}
else if ( dot < -epsilon ) {
else if ( dists[i] < -epsilon ) {
sides[i] = SIDE_BACK;
}
else{
@@ -282,38 +290,38 @@ bool DWinding::ChopWindingInPlace( DPlane* chopPlane, vec_t epsilon ){
counts[sides[i]]++;
}
sides[i] = sides[0];
dists[i] = dists[0];
sides[numpoints] = sides[0];
dists[numpoints] = dists[0];
if ( !counts[0] ) {
delete this;
if ( !counts[SIDE_FRONT] ) {
*this = DWinding();
return false;
}
if ( !counts[1] ) {
if ( !counts[SIDE_BACK] ) {
return true;
}
int maxpts = numpoints + 4; // cant use counts[0]+2 because
// of fp grouping errors
const int maxpts = numpoints + 4; // cant use counts[0]+2 because
// of fp grouping errors
DWinding* f = new DWinding;
f->AllocWinding( maxpts );
f->numpoints = 0;
DWinding f;
f.AllocWinding( maxpts );
f.numpoints = 0;
for ( i = 0; i < numpoints; i++ )
for ( int i = 0; i < numpoints; i++ )
{
p1 = p[i];
const vec3_t& p1 = p[i];
if ( sides[i] == SIDE_ON ) {
VectorCopy( p1, f->p[f->numpoints] );
f->numpoints++;
VectorCopy( p1, f.p[f.numpoints] );
f.numpoints++;
continue;
}
if ( sides[i] == SIDE_FRONT ) {
VectorCopy( p1, f->p[f->numpoints] );
f->numpoints++;
VectorCopy( p1, f.p[f.numpoints] );
f.numpoints++;
}
if ( sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i] ) {
@@ -321,9 +329,10 @@ bool DWinding::ChopWindingInPlace( DPlane* chopPlane, vec_t epsilon ){
}
// generate a split point
p2 = p[( i + 1 ) % numpoints];
const vec3_t& p2 = p[( i + 1 ) % numpoints];
vec_t dot = dists[i] / ( dists[i] - dists[i + 1] );
const vec_t dot = dists[i] / ( dists[i] - dists[i + 1] );
vec3_t mid;
for ( int j = 0; j < 3; j++ )
{
if ( chopPlane->normal[j] == 1 ) {
@@ -337,21 +346,18 @@ bool DWinding::ChopWindingInPlace( DPlane* chopPlane, vec_t epsilon ){
}
}
VectorCopy( mid, f->p[f->numpoints] );
f->numpoints++;
VectorCopy( mid, f.p[f.numpoints] );
f.numpoints++;
}
if ( f->numpoints > maxpts ) {
globalWarningStream() << "ClipWinding: points exceeded estimate\n";
if ( f.numpoints > maxpts ) {
globalWarningStream() << "ChopWindingInPlace: points exceeded estimate\n";
}
if ( f->numpoints > MAX_POINTS_ON_WINDING ) {
globalWarningStream() << "ClipWinding: MAX_POINTS_ON_WINDING\n";
if ( f.numpoints > MAX_POINTS_ON_WINDING ) {
globalWarningStream() << "ChopWindingInPlace: MAX_POINTS_ON_WINDING\n";
}
delete[] p;
p = f->p;
f->p = NULL;
delete f;
*this = std::move( f );
return true;
}
+3 -2
View File
@@ -31,6 +31,8 @@ class DWinding
{
public:
DWinding();
DWinding( DWinding&& other ) noexcept;
DWinding& operator=( DWinding&& other ) noexcept;
virtual ~DWinding();
void AllocWinding( int points );
@@ -55,9 +57,8 @@ public:
// members
int numpoints;
vec3_t* p;
vec3_t clr;
};
#define MAX_POINTS_ON_WINDING 64
#define MAX_POINTS_ON_WINDING 256
#define ON_EPSILON 0.01
+48 -20
View File
@@ -26,9 +26,15 @@ dbrush_t *dbrushes = NULL;
dbrushside_t *dbrushsides = NULL;
int *dleafbrushes = NULL;
#define BSP_IDENT ( ( 'P' << 24 ) + ( 'S' << 16 ) + ( 'B' << 8 ) + 'I' )
#define Q3_BSP_VERSION 46
#define WOLF_BSP_VERSION 47
#define IBSP_IDENT ( ( 'P' << 24 ) + ( 'S' << 16 ) + ( 'B' << 8 ) + 'I' )
#define IBSP_VERSION_Q3 46
#define IBSP_VERSION_WOLF 47 // also quakelive
// jka, jk2, sof2
#define RBSP_IDENT ( ( 'P' << 24 ) + ( 'S' << 16 ) + ( 'B' << 8 ) + 'R' )
#define RBSP_VERSION 1
// warsow
#define FBSP_IDENT ( ( 'P' << 24 ) + ( 'S' << 16 ) + ( 'B' << 8 ) + 'F' )
#define FBSP_VERSION 1
/*
================
@@ -204,33 +210,55 @@ bool LoadBSPFile( const char *filename ) {
// load the file header
if ( !LoadFile( filename, (byte **)&header ) ) {
DoMessageBox( "BSP file not found", "Error", EMessageBoxType::Error );
return false;
}
// swap the header
SwapBlock( (int *)header, sizeof( *header ) );
if ( header->ident != BSP_IDENT ) {
DoMessageBox( "Cant find a valid IBSP file", "Error", EMessageBoxType::Error );
return false;
}
if ( ( header->version != Q3_BSP_VERSION ) &&
( header->version != WOLF_BSP_VERSION ) ) {
DoMessageBox( "File is incorrect version", "Error", EMessageBoxType::Error );
if ( header->ident != IBSP_IDENT
&& header->ident != RBSP_IDENT
&& header->ident != FBSP_IDENT ) {
DoMessageBox( "Cant find a valid IBSP/RBSP/FBSP file", "Error", EMessageBoxType::Error );
delete[] header;
return false;
}
numbrushsides = CopyLump( header, LUMP_BRUSHES, (void**)&dbrushsides, sizeof( dbrushside_t ) );
numbrushes = CopyLump( header, LUMP_BRUSHES, (void**)&dbrushes, sizeof( dbrush_t ) );
numplanes = CopyLump( header, LUMP_PLANES, (void**)&dplanes, sizeof( dplane_t ) );
numleafs = CopyLump( header, LUMP_LEAFS, (void**)&dleafs, sizeof( dleaf_t ) );
numnodes = CopyLump( header, LUMP_NODES, (void**)&dnodes, sizeof( dnode_t ) );
numDrawVerts = CopyLump( header, LUMP_DRAWVERTS, (void**)&drawVerts, sizeof( qdrawVert_t ) );
if ( !( header->ident == IBSP_IDENT && ( header->version == IBSP_VERSION_Q3 || header->version == IBSP_VERSION_WOLF ) )
&& !( header->ident == RBSP_IDENT && header->version == RBSP_VERSION )
&& !( header->ident == FBSP_IDENT && header->version == FBSP_VERSION ) ) {
DoMessageBox( "File is incorrect version", "Error", EMessageBoxType::Error );
delete[] header;
return false;
}
if( header->ident == IBSP_IDENT )
numbrushsides = CopyLump( header, LUMP_BRUSHES, (void**)&dbrushsides, sizeof( dbrushside_t ) );
else{
numbrushsides = CopyLump( header, LUMP_BRUSHES, (void**)&dbrushsides, sizeof( rbspBrushSide_t ) );
std::copy_n( (rbspBrushSide_t*)dbrushsides, numbrushes, dbrushsides );
}
numbrushes = CopyLump( header, LUMP_BRUSHES, (void**)&dbrushes, sizeof( dbrush_t ) );
numplanes = CopyLump( header, LUMP_PLANES, (void**)&dplanes, sizeof( dplane_t ) );
numleafs = CopyLump( header, LUMP_LEAFS, (void**)&dleafs, sizeof( dleaf_t ) );
numnodes = CopyLump( header, LUMP_NODES, (void**)&dnodes, sizeof( dnode_t ) );
if( header->ident == IBSP_IDENT )
numDrawVerts = CopyLump( header, LUMP_DRAWVERTS, (void**)&drawVerts, sizeof( qdrawVert_t ) );
else{
numDrawVerts = CopyLump( header, LUMP_DRAWVERTS, (void**)&drawVerts, sizeof( rbspDrawVert_t ) );
std::copy_n( (rbspDrawVert_t*)drawVerts, numDrawVerts, drawVerts );
}
numDrawVertsIndices = CopyLump( header, LUMP_DRAWINDEXES, (void**)&drawVertsIndices, sizeof( int ) );
numDrawSurfaces = CopyLump( header, LUMP_SURFACES, (void**)&drawSurfaces, sizeof( dsurface_t ) );
numleafsurfaces = CopyLump( header, LUMP_LEAFSURFACES, (void**)&dleafsurfaces, sizeof( int ) );
numVisBytes = CopyLump( header, LUMP_VISIBILITY, (void**)&visBytes, 1 );
numleafbrushes = CopyLump( header, LUMP_LEAFBRUSHES, (void**)&dleafbrushes, sizeof( int ) );
if( header->ident == IBSP_IDENT )
numDrawSurfaces = CopyLump( header, LUMP_SURFACES, (void**)&drawSurfaces, sizeof( dsurface_t ) );
else{
numDrawSurfaces = CopyLump( header, LUMP_SURFACES, (void**)&drawSurfaces, sizeof( rbspDrawSurface_t ) );
std::copy_n( (rbspDrawSurface_t*)drawSurfaces, numDrawSurfaces, drawSurfaces );
}
numleafsurfaces = CopyLump( header, LUMP_LEAFSURFACES, (void**)&dleafsurfaces, sizeof( int ) );
numVisBytes = CopyLump( header, LUMP_VISIBILITY, (void**)&visBytes, 1 );
numleafbrushes = CopyLump( header, LUMP_LEAFBRUSHES, (void**)&dleafbrushes, sizeof( int ) );
delete[] header; // everything has been copied out
+75
View File
@@ -67,6 +67,26 @@ typedef struct {
byte color[4];
} qdrawVert_t;
#define MAX_LIGHTMAPS 4
struct rbspDrawVert_t
{
vec3_t xyz;
float st[2];
float lightmap[ MAX_LIGHTMAPS ][2]; /* RBSP */
vec3_t normal;
byte color[ MAX_LIGHTMAPS ][4]; /* RBSP */
operator qdrawVert_t() const {
return{
{ xyz[0], xyz[1], xyz[2] },
{ st[0], st[1] },
{ lightmap[0][0], lightmap[0][1] },
{ normal[0], normal[1], normal[2] },
{ color[0][0], color[0][1], color[0][2], color[0][3] }
};
}
};
typedef struct {
int shaderNum;
int fogNum;
@@ -89,11 +109,66 @@ typedef struct {
int patchHeight;
} dsurface_t;
struct rbspDrawSurface_t
{
int shaderNum;
int fogNum;
int surfaceType;
int firstVert;
int numVerts;
int firstIndex;
int numIndexes;
byte lightmapStyles[ MAX_LIGHTMAPS ]; /* RBSP */
byte vertexStyles[ MAX_LIGHTMAPS ]; /* RBSP */
int lightmapNum[ MAX_LIGHTMAPS ]; /* RBSP */
int lightmapX[ MAX_LIGHTMAPS ], lightmapY[ MAX_LIGHTMAPS ]; /* RBSP */
int lightmapWidth, lightmapHeight;
vec3_t lightmapOrigin;
vec3_t lightmapVecs[ 3 ]; /* on patches, [ 0 ] and [ 1 ] are lodbounds */
int patchWidth;
int patchHeight;
operator dsurface_t() const {
return{
shaderNum,
fogNum,
surfaceType,
firstVert,
numVerts,
firstIndex,
numIndexes,
lightmapNum[0],
lightmapX[0], lightmapY[0],
lightmapWidth, lightmapHeight,
{ lightmapOrigin[0], lightmapOrigin[1], lightmapOrigin[2] },
{ { lightmapVecs[0][0], lightmapVecs[0][1], lightmapVecs[0][2] },
{ lightmapVecs[1][0], lightmapVecs[1][1], lightmapVecs[1][2] },
{ lightmapVecs[2][0], lightmapVecs[2][1], lightmapVecs[2][2] } },
patchWidth,
patchHeight
};
}
};
typedef struct {
int planeNum; // positive plane side faces out of the leaf
int shaderNum;
} dbrushside_t;
struct rbspBrushSide_t
{
int planeNum; /* positive plane side faces out of the leaf */
int shaderNum;
int surfaceNum; /* RBSP */
operator dbrushside_t() const {
return { planeNum, shaderNum };
}
};
typedef struct {
int firstSide;
int numSides;
+6 -18
View File
@@ -22,6 +22,8 @@
#include "dialogs/dialogs-gtk.h"
#include "string/string.h"
#include "stream/stringstream.h"
#include "os/path.h"
#include "DPoint.h"
#include "DPlane.h"
@@ -616,31 +618,17 @@ void DoVisAnalyse(){
return;
}
char filename[1024];
strcpy( filename, rad_filename );
char* ext = strrchr( filename, '.' ) + 1;
strcpy( ext, "bsp" ); // rename the extension
const auto filename = StringStream<256>( PathExtensionless( rad_filename ), ".bsp" );
vec3_t origin;
if ( GlobalSelectionSystem().countSelected() == 0 ) {
memcpy( origin, GlobalRadiant().Camera_getOrigin().data(), 3 * sizeof( Vector3().x() ) );
std::copy_n( GlobalRadiant().Camera_getOrigin().data(), 3, origin );
}
else{
memcpy( origin, GlobalSelectionSystem().getBoundsSelected().origin.data(), 3 * sizeof( Vector3().x() ) );
std::copy_n( GlobalSelectionSystem().getBoundsSelected().origin.data(), 3, origin );
}
DMetaSurfaces* pointList = BuildTrace( filename, origin );
if( pointList && pointList->size() )
globalOutputStream() << "bobToolz VisAnalyse: " << pointList->size() << " drawsurfaces loaded\n";
if ( !g_VisView ) {
g_VisView = std::make_unique<DVisDrawer>();
}
g_VisView->SetList( pointList );
SceneChangeNotify();
SetupVisView( filename, origin );
}
void DoTrainPathPlot() {
+276 -102
View File
@@ -3,13 +3,12 @@
// Written by: Brad Whitehead (whiteheb@gamerstv.net)
#include "visfind.h"
#include "dialogs/dialogs-gtk.h"
#include "bsploader.h"
#include "DVisDrawer.h"
#include "funchandlers.h"
#include "DPlane.h"
typedef struct {
int portalclusters;
int leafbytes; //leafbytes = ((portalclusters+63)&~63)>>3;
int portalclusters; //number of clusters
int leafbytes; //size of data per cluster //leafbytes = ((portalclusters+63)&~63)>>3; //multiple of 8
} vis_header;
// added because int shift = 32; i = 0xFFFFFFFF >> shift;
@@ -57,12 +56,62 @@ int bsp_leafnumfororigin( vec3_t origin ){
return 0;
}
int bsp_leafnumforcluster( int cluster ){
dleaf_t *l;
int i;
std::vector<dplane_t*> bsp_planesForLeaf( int leafnum ){
std::vector<dplane_t*> planes;
for ( i = 0, l = dleafs; i < numleafs; i++, l++ )
if ( l->cluster == cluster ) {
int findnum = -( leafnum + 1 );
auto cmp = [&]( const dnode_t& node ){
return
findnum == node.children[0]
? ( planes.push_back( &dplanes[node.planeNum] ), true ) //inverse of plane, which would work for convex hull; to match ChopWindingInPlace 🤔
: findnum == node.children[1]
? ( planes.push_back( &dplanes[node.planeNum ^ 1] ), true ) //inverse of plane, which would work for convex hull; to match ChopWindingInPlace 🤔
: false;
};
dnode_t *dnodesend = dnodes + numnodes;
dnode_t *node = std::find_if( dnodes, dnodesend, cmp );
while( node != dnodesend ){
findnum = node - dnodes;
node = std::find_if( dnodes, dnodesend, cmp );
};
return planes;
}
std::vector<DWinding> BuildLeafWindings( int leafnum ){
std::vector<DWinding> windings;
std::vector<dplane_t*> planes = bsp_planesForLeaf( leafnum );
DPlane dplane;
for( const dplane_t *plane : planes )
{
VectorCopy( plane->normal, dplane.normal );
dplane._d = plane->dist;
DWinding winding = dplane.BaseWindingForPlane();
for( const dplane_t *cplane : planes )
{
if( winding.numpoints <= 2 ){
break;
}
if( cplane != plane ){
VectorCopy( cplane->normal, dplane.normal );
dplane._d = cplane->dist;
winding.ChopWindingInPlace( &dplane, .1f );
}
}
if( winding.numpoints > 2 ){
windings.push_back( std::move( winding ) );
}
}
return windings;
}
int bsp_leafnumforcluster( int cluster ){
for ( int i = 0; i < numleafs; ++i )
if ( dleafs[ i ].cluster == cluster ) {
return( i );
}
return( 0 );
@@ -124,87 +173,112 @@ int bsp_countclusters_mask( byte *bitvector, byte *maskvector, int length ){
return( c );
}
void AddCluster( DMetaSurfaces* pointlist, dleaf_t *cl, bool* repeatlist, const vec3_t clr ){
int* leafsurf = &dleafsurfaces[cl->firstLeafSurface];
for ( int k = 0; k < cl->numLeafSurfaces; k++, leafsurf++ )
{
if ( repeatlist[*leafsurf] ) {
continue;
}
dsurface_t* surf = &drawSurfaces[*leafsurf];
if ( surf->surfaceType != MST_PLANAR ) {
continue;
}
qdrawVert_t* vert = &drawVerts[surf->firstVert];
if ( surf->firstVert + surf->numVerts > numDrawVerts ) {
DoMessageBox( "Warning", "Warning", EMessageBoxType::Warning );
}
DMetaSurf* meta = new DMetaSurf( surf->numVerts, surf->numIndexes );
for ( int l = 0; l < surf->numIndexes; ++l )
meta->indices[l] = drawVertsIndices[ surf->firstIndex + l ];
for ( int l = 0; l < surf->numVerts; ++l, ++vert )
VectorCopy( vert->xyz, meta->verts[l] );
VectorCopy( clr, meta->colour );
pointlist->push_back( meta );
repeatlist[*leafsurf] = true;
}
inline bool bit_is_enabled( const byte *bytes, int bit_index ){
return ( bytes[bit_index >> 3] & ( 1 << ( bit_index & 7 ) ) ) != 0;
}
/*
=============
CreateTrace
=============
*/
DMetaSurfaces *CreateTrace( dleaf_t *leaf, int c, vis_header *header, byte *visdata, byte *seen ){
byte *vis;
int i, j, clusterNum;
DMetaSurfaces* pointlist = new DMetaSurfaces;
bool* repeatlist = new bool[numDrawSurfaces];
dleaf_t *cl;
static void PutPointsOnCurve( vec3_t ctrl[], int width, int height ) {
const auto lerp3 = []( const vec3_t prev, vec3_t mid, const vec3_t next ){
mid[0] = ( prev[0] + mid[0] * 2 + next[0] ) * .25;
mid[1] = ( prev[1] + mid[1] * 2 + next[1] ) * .25;
mid[2] = ( prev[2] + mid[2] * 2 + next[2] ) * .25;
};
// put all the aproximating points on the curve
for ( int i = 0; i < width; ++i )
for ( int j = 1; j < height; j += 2 )
lerp3( ctrl[( j - 1 ) * width + i],
ctrl[( j + 0 ) * width + i],
ctrl[( j + 1 ) * width + i] );
const vec3_t clrGreen = {0.f, 1.f, 0.f};
for ( int j = 0; j < height; ++j )
for ( int i = 1; i < width; i += 2 )
lerp3( ctrl[j * width + i - 1],
ctrl[j * width + i + 0],
ctrl[j * width + i + 1] );
}
memset( repeatlist, 0, sizeof( bool ) * numDrawSurfaces );
vis = visdata + ( c * header->leafbytes );
clusterNum = 0;
AddCluster( pointlist, &( dleafs[bsp_leafnumforcluster( c )] ), repeatlist, clrGreen );
for ( i = 0; i < header->leafbytes; i++ )
const vec3_t debugColorMain = { 0, 255, 0 };
const vec3_t debugColors[ 16 ] =
{
for ( j = 0; j < 8; j++ )
{
cl = &( dleafs[bsp_leafnumforcluster( clusterNum )] );
{ 1 , 0 , 0 },
{ 0.75, 0.5 , 0.5 },
{ 1 , 1 , 0 },
{ 0.75, 0.75, 0.5 },
{ 0 , 1 , 1 },
{ 0.5 , 0.75, 0.75 },
{ 0 , 0 , 1 },
{ 0.5 , 0.5 , 0.75 },
{ 1 , 0 , 1 },
{ 0.75, 0.5 , 0.75 },
{ 0.5 , 0.75, 0.5 },
{ 0.75, 0 , 0.5 },
{ 0.5 , 0 , 0.75 },
{ 0 , 0.5 , 0.75 },
{ 0.75, 0.5 , 0 },
{ 1 , 0 , 0.5 },
};
#if 0
vec3_t clr;
do {
VectorSet( clr,
( rand() % 256 ) / 255.f,
( rand() % 256 ) / 255.f,
( rand() % 256 ) / 255.f );
} while( ( clr[0] + clr[2] < clr[1] * 1.5f ) || //too green
( clr[0] < .3 && clr[1] < .3 && clr[2] < .3 ) ); //too dark
#endif
if ( ( *( vis + i ) & ( 1 << j ) ) && ( *( seen + i ) & ( 1 << j ) ) && ( leaf->area == cl->area ) ) {
vec3_t clr;
do {
VectorSet( clr,
( rand() % 256 ) / 255.f,
( rand() % 256 ) / 255.f,
( rand() % 256 ) / 255.f );
} while( ( clr[0] + clr[2] < clr[1] * 1.5f ) || //too green
( clr[0] < .3 && clr[1] < .3 && clr[2] < .3 ) ); //too dark
AddCluster( pointlist, cl, repeatlist, clr );
void AddSurface( const dsurface_t& surf, const vec3_t clr, DMetaSurfaces* pointlist ){
if ( surf.surfaceType == MST_PLANAR || surf.surfaceType == MST_TRIANGLE_SOUP ) {
qdrawVert_t* vert = &drawVerts[surf.firstVert];
DMetaSurf& meta = pointlist->emplace_back( surf.numVerts, surf.numIndexes );
for ( int l = 0; l < surf.numIndexes; ++l )
meta.indices[l] = drawVertsIndices[ surf.firstIndex + l ];
for ( int l = 0; l < surf.numVerts; ++l, ++vert )
VectorCopy( vert->xyz, meta.verts[l] );
VectorCopy( clr, meta.colour );
}
else if ( surf.surfaceType == MST_PATCH ) {
qdrawVert_t* vert = &drawVerts[surf.firstVert];
DMetaSurf& meta = pointlist->emplace_back( surf.numVerts, ( surf.patchWidth - 1 ) * ( surf.patchHeight - 1 ) * 2 * 3 );
for ( int l = 0; l < surf.numVerts; ++l, ++vert )
VectorCopy( vert->xyz, meta.verts[l] );
VectorCopy( clr, meta.colour );
PutPointsOnCurve( meta.verts, surf.patchWidth, surf.patchHeight );
unsigned int *idx = meta.indices;
for ( int i = 0; i < surf.patchWidth - 1; ++i ){
for ( int j = 0; j < surf.patchHeight - 1; ++j ){
idx[0] = ( j + 0 ) * surf.patchWidth + i;
idx[1] = ( j + 1 ) * surf.patchWidth + i;
idx[2] = ( j + 1 ) * surf.patchWidth + i + 1;
idx[3] = idx[0];
idx[4] = idx[2];
idx[5] = ( j + 0 ) * surf.patchWidth + i + 1;
idx += 6;
}
clusterNum++;
}
}
};
delete [] repeatlist;
return pointlist;
void AddLeafSurfaces( const dleaf_t& leaf, const vec3_t clr, bool colorPerSurf, bool* repeatlist, DMetaSurfaces* pointlist ){
for( int leafsurf : std::span( dleafsurfaces + leaf.firstLeafSurface, leaf.numLeafSurfaces ) )
{
if ( !repeatlist[leafsurf] ) {
repeatlist[leafsurf] = true;
AddSurface( drawSurfaces[leafsurf], colorPerSurf? debugColors[leafsurf & 15] : clr, pointlist );
}
}
}
#include "timer.h"
/*
=============
TraceCluster
@@ -212,45 +286,145 @@ DMetaSurfaces *CreateTrace( dleaf_t *leaf, int c, vis_header *header, byte *visd
setup for CreateTrace
=============
*/
DMetaSurfaces* TraceCluster( int leafnum ){
byte seen[( MAX_MAP_LEAFS / 8 ) + 1];
vis_header *vheader;
class TraceCluster
{
const vis_header vheader;
byte *visdata;
dleaf_t *leaf;
bool *repeatlist;
void repeatlist_clear() const {
memset( repeatlist, 0, sizeof( bool ) * numDrawSurfaces );
}
public:
TraceCluster()
: vheader( *(vis_header *) visBytes ),
visdata( visBytes + sizeof( vis_header ) ),
repeatlist( new bool[numDrawSurfaces] )
{}
~TraceCluster(){
delete [] repeatlist;
}
TraceCluster( TraceCluster&& ) noexcept = delete;
vheader = (vis_header *) visBytes;
visdata = visBytes + sizeof( vis_header );
DMetaSurfaces* doTraceCluster( int leafnum, bool colorPerSurf ) const {
DMetaSurfaces* pointlist = new DMetaSurfaces;
memset( seen, 0xFF, sizeof( seen ) );
bsp_setbitvectorlength( seen, vheader->portalclusters, sizeof( seen ) );
repeatlist_clear();
leaf = &( dleafs[leafnum] );
const dleaf_t& leaf = dleafs[leafnum];
const byte *vis = visdata + ( leaf.cluster * vheader.leafbytes );
return CreateTrace( leaf, leaf->cluster, vheader, visdata, seen );
}
AddLeafSurfaces( leaf, debugColorMain, false, repeatlist, pointlist );
DMetaSurfaces* BuildTrace( char* filename, vec3_t v_origin ){
if ( !LoadBSPFile( filename ) ) {
return NULL;
for ( const dleaf_t& cleaf : std::span( dleafs, numleafs ) )
{
if ( cleaf.cluster >= 0 && cleaf.cluster < vheader.portalclusters
&& leaf.area == cleaf.area
&& bit_is_enabled( vis, cleaf.cluster ) )
{
AddLeafSurfaces( cleaf, debugColors[( &cleaf - dleafs )%15], colorPerSurf, repeatlist, pointlist );
}
}
return pointlist;
}
if( numVisBytes == 0 ){
FreeBSPData();
globalErrorStream() << "bobToolz VisAnalyse: Bsp has no visibility data!\n";
return 0;
void traverseLeafs( DVisDrawer& drawer ) const {
Timer timer;
std::vector<int> shadernums;
int nsurfs, nleafs;
for( const dleaf_t& leaf : std::span( dleafs, numleafs ) ){
if( leaf.cluster >= 0 && leaf.cluster < vheader.portalclusters ){
nsurfs = nleafs = 0;
shadernums.clear();
auto countLeafStuff = [&nsurfs, &shadernums, this]( const dleaf_t& leaf ){
for( int leafsurf : std::span( dleafsurfaces + leaf.firstLeafSurface, leaf.numLeafSurfaces ) )
{
if ( !repeatlist[leafsurf] ) {
repeatlist[leafsurf] = true;
const dsurface_t& surf = drawSurfaces[leafsurf];
if( surf.surfaceType != MST_BAD )
++nsurfs;
if( std::ranges::find( shadernums, surf.shaderNum ) == shadernums.cend() )
shadernums.push_back( surf.shaderNum );
}
}
};
repeatlist_clear();
const byte *vis = visdata + ( leaf.cluster * vheader.leafbytes );
countLeafStuff( leaf );
for ( const dleaf_t& cleaf : std::span( dleafs, numleafs ) )
{
if ( cleaf.cluster >= 0 && cleaf.cluster < vheader.portalclusters
&& leaf.area == cleaf.area
&& bit_is_enabled( vis, cleaf.cluster ) )
{
countLeafStuff( cleaf );
++nleafs;
}
}
drawer.ui_leaf_add( &leaf - dleafs, nleafs, nsurfs, shadernums.size() );
}
}
globalOutputStream() << timer.elapsed_msec() << " timer.elapsed_msec()\n";
}
};
void SetupVisView( const char* filename, vec3_t v_origin ){
/* g_VisView and LoadBSPFile must have same lifetime */
if( !g_VisView ){
if ( !LoadBSPFile( filename ) ) {
return;
}
if( numVisBytes <= (int)sizeof( vis_header ) ){
globalErrorStream() << "bobToolz VisAnalyse: Bsp has no visibility data!\n";
int maxcluster = -1;
std::for_each_n( dleafs, numleafs, [&maxcluster]( const dleaf_t& leaf ){ maxcluster = std::max( maxcluster, leaf.cluster ); } );
if( maxcluster >= 0 ){
globalErrorStream() << "bobToolz VisAnalyse: Setting up fake visibility data!\n";
delete[] visBytes;
vis_header vheader;
vheader.portalclusters = maxcluster + 1;
vheader.leafbytes = ( ( vheader.portalclusters + 63 ) & ~63 ) >> 3;
numVisBytes = sizeof( vis_header ) + vheader.portalclusters * vheader.leafbytes;
visBytes = new byte[ numVisBytes ];
memset( visBytes, 0xFF, numVisBytes );
*( vis_header* )visBytes = vheader;
}
else{
FreeBSPData();
return;
}
}
g_VisView = std::make_unique<DVisDrawer>();
TraceCluster traceCluster;
traceCluster.traverseLeafs( *g_VisView );
g_VisView->ui_show();
}
int leafnum = bsp_leafnumfororigin( v_origin );
const int leafnum = bsp_leafnumfororigin( v_origin );
if( dleafs[leafnum].cluster == -1 ){
FreeBSPData();
g_VisView->ClearPoints();
globalErrorStream() << "bobToolz VisAnalyse: Point of interest is in the void!\n";
return 0;
return;
}
DMetaSurfaces* pointlist = TraceCluster( leafnum );
g_VisView->ui_leaf_show( leafnum );
}
FreeBSPData();
return pointlist;
DMetaSurfaces* BuildTrace( int leafnum, bool colorPerSurf ){
TraceCluster traceCluster;
return traceCluster.doTraceCluster( leafnum, colorPerSurf );
}
+6 -6
View File
@@ -1,10 +1,10 @@
#pragma once
#include <list>
#include "mathlib.h"
#include "DVisDrawer.h"
#include "bsploader.h"
#include "DWinding.h"
class DMetaSurf;
typedef std::list<DMetaSurf*> DMetaSurfaces;
DMetaSurfaces* BuildTrace( char* filename, vec3_t v_origin );
void SetupVisView( const char* filename, vec3_t v_origin );
DMetaSurfaces* BuildTrace( int leafnum, bool colorPerSurf );
std::vector<DWinding> BuildLeafWindings( int leafnum );
+7
View File
@@ -1116,6 +1116,13 @@ misc...
* add bobToolz.explodeEntity: create group entity from each primitive of existing group entity
* improve CSG Subtract results via more defined splits order
* hide group entity node when all its children get hidden
* bobToolz::Vis Viewer add sortable leafs chart (visible leafs, surfaces, shaders)
add rendering of MST_PATCH, MST_TRIANGLE_SOUP
use consistent color per colorable object
option to assing color per surface, not leaf (to debug surfaces)(current leaf surfs are always green anyway)
render current leaf convex volume, in white
support bsp without vis data
add RBSP, FBSP support