You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegraphics/kpovmodeler/pmheightfieldroam.cpp

423 lines
9.7 KiB

/*
**************************************************************************
description
--------------------
copyright : (C) 2003 by Leon Pennington
email : leon@leonscape.co.uk
**************************************************************************
**************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
**************************************************************************/
#include "pmheightfieldroam.h"
#include <cstdlib>
#include <new>
#include <tqstring.h>
#include <tqimage.h>
#include <tqcolor.h>
#include <kdebug.h>
PMHeightFieldROAM::PMHeightFieldROAM( const TQString &fileName )
{
m_size = 0;
m_numLines = m_usedPoints = 0;
m_waterLevel = m_displayDetail = 0;
m_mapMod = m_levelMod = true;
m_fail = false;
m_pPoints = 0;
m_pTree = 0;
if ( !imageToData( fileName ) )
{
if ( m_pPoints )
delete [] m_pPoints;
if ( m_pTree )
delete [] m_pTree;
m_pPoints = 0;
m_pTree = 0;
m_pNextNode = 0;
m_size = m_numPoints = m_numNodes = 0;
m_fail = true;
return;
}
calcLevel( );
}
PMHeightFieldROAM::~PMHeightFieldROAM( )
{
delete [] m_pPoints;
delete [] m_pTree;
}
void PMHeightFieldROAM::updateModel( )
{
if ( m_fail )
return;
int z = m_size - 1;
if ( m_mapMod )
{
m_mapMod = false;
m_levelMod = false;
clearPoints( );
clearNodes( true );
m_pNextNode = m_pTree + 1;
m_pNextNode->base = m_pTree;
m_pTree->base = m_pNextNode++;
varNode(m_pTree, z, 0, 0, 0, 0, z, 0);
varNode(m_pTree + 1, 0, z, z, z, z, 0, 0);
sptNode(m_pTree, 0);
sptNode(m_pTree + 1, 0);
pntNode(m_pTree, z, 0, 0, 0, 0, z);
pntNode(m_pTree + 1, 0, z, z, z, z, 0);
}
else if ( m_levelMod )
{
m_levelMod = false;
clearPoints( );
clearNodes( );
sptNode( m_pTree, 0 );
sptNode( m_pTree + 1, 0 );
pntNode( m_pTree, z, 0, 0, 0, 0, z );
pntNode( m_pTree + 1, 0, z, z, z, z, 0 );
}
}
void PMHeightFieldROAM::setDisplayDetail( int detail )
{
if ( detail != m_displayDetail )
{
m_displayDetail = detail;
m_levelMod = true;
}
}
void PMHeightFieldROAM::setWaterLevel( double waterLevel )
{
int waterConv = (int)( waterLevel * 65535 );
if ( waterConv != m_waterLevel )
{
m_waterLevel = waterConv;
m_levelMod = true;
}
}
double PMHeightFieldROAM::waterLevel( ) const
{
double waterConv = m_waterLevel / 65535.0;
return waterConv;
}
unsigned short PMHeightFieldROAM::height( int x, int y, bool waterLevel ) const
{
unsigned short hgt = m_pPoints[ x + ( y * m_size ) ].hgt;
if ( waterLevel )
{
if ( hgt <= m_waterLevel )
return m_waterLevel;
else
return hgt;
}
else
return hgt;
}
bool PMHeightFieldROAM::lineExist( int x, int y, int line ) const
{
if ( m_pPoints[ x + ( y * m_size ) ].lines[ line ] ) return true;
else return false;
}
bool PMHeightFieldROAM::imageToData(const TQString &fileName)
{
TQImage scaledMap;
TQImage mapFile( fileName );
if ( mapFile.isNull( ) )
return false;
if ( mapFile.width( ) > 192 || mapFile.height( ) > 192 )
{
scaledMap = mapFile.scale( 257, 257 );
}
else if ( mapFile.width( ) > 96 || mapFile.height( ) > 96 )
{
scaledMap = mapFile.scale( 129, 129 );
}
else if ( mapFile.width( ) > 48 || mapFile.height( ) > 48)
{
scaledMap = mapFile.scale( 65, 65 );
}
else
{
scaledMap = mapFile.scale( 33, 33 );
}
if ( scaledMap.isNull( ) )
return false;
m_size = scaledMap.width( );
if ( !createPoints( ) || !createNodes( ) )
return false;
bool colourIndex;
if ( mapFile.depth( ) > 8 )
colourIndex = false;
else
{
scaledMap = scaledMap.convertDepthWithPalette( 8, mapFile.tqcolorTable( ), 256 );
colourIndex = true;
}
for ( int y = 0, y2 = ( m_size - 1 ) ; y < m_size ; ++y, --y2 )
{
for ( int x = 0 ; x < m_size ; ++x )
{
if ( colourIndex )
setHeight( x, y2, scaledMap.pixelIndex( x, y ) * 256 );
else
setHeight( x, y2, ( 256 * tqRed( scaledMap.pixel( x, y ) ) ) +
tqGreen( scaledMap.pixel( x, y ) ) );
}
}
return true;
}
void PMHeightFieldROAM::calcLevel( )
{
int i = 0;
int j = m_size;
while( j != 1)
{
j /= 2;
i++;
}
m_maxLevel = i * 2;
}
void PMHeightFieldROAM::varNode ( triNodeStructure* current,
int x1, int y1,
int x2, int y2,
int x3, int y3,
int level )
{
int xm = (x1 + x3) >> 1;
int ym = (y1 + y3) >> 1;
if ( level >= m_maxLevel )
{
unsigned short z1 = height( x1, y1 );
unsigned short z3 = height( x3, y3 );
unsigned short zm = ( ( z3 - z1 ) / 2 ) + z1;
unsigned short hgt = height( xm, ym );
current->vari = abs( zm - hgt );
return;
}
current->lchd = m_pNextNode++;
current->rchd = m_pNextNode++;
varNode(current->lchd, x3, y3, xm, ym, x2, y2, level + 1);
varNode(current->rchd, x2, y2, xm, ym, x1, y1, level + 1);
current->vari = current->lchd->vari + current->rchd->vari;
}
void PMHeightFieldROAM::sptNode ( triNodeStructure* current, int level )
{
if ( !current->split )
{
if ( level >= m_maxLevel ) return;
if (current->vari > m_displayDetail) split(current);
else return;
}
sptNode(current->lchd, level + 1);
sptNode(current->rchd, level + 1);
}
void PMHeightFieldROAM::split( triNodeStructure* current )
{
current->split = true;
if ( current->base )
{
if ( current->base->base != current ) split( current->base );
}
triNodeStructure* child;
//left child
child = current->lchd;
child->base = current->lnbr;
if ( current->lnbr )
{
if ( current->lnbr->rnbr == current ) current->lnbr->rnbr = child;
else current->lnbr->base = child;
}
child->lnbr = current->rchd;
//rightchild
child = current->rchd;
child->base = current->rnbr;
if ( current->rnbr )
{
if ( current->rnbr->lnbr == current ) current->rnbr->lnbr = child;
else current->rnbr->base = child;
}
child->rnbr = current->lchd;
if ( current->base )
{
if ( !current->base->split ) split( current->base );
current->lchd->rnbr = current->base->rchd;
current->rchd->lnbr = current->base->lchd;
}
}
void PMHeightFieldROAM::pntNode( triNodeStructure* current,
int x1, int y1,
int x2, int y2,
int x3, int y3 )
{
if (current->split)
{
int xm = (x1 + x3) >> 1;
int ym = (y1 + y3) >> 1;
pntNode( current->lchd, x3, y3, xm, ym, x2, y2 );
pntNode( current->rchd, x2, y2, xm, ym, x1, y1 );
}
else
{
pointStructure* pts[3];
pts[0] = &m_pPoints[ x1 + ( y1 * m_size ) ];
pts[1] = &m_pPoints[ x2 + ( y2 * m_size ) ];
pts[2] = &m_pPoints[ x3 + ( y3 * m_size ) ];
if ( m_waterLevel != 0 )
{
if ( pts[0]->hgt <= m_waterLevel &&
pts[1]->hgt <= m_waterLevel &&
pts[2]->hgt <= m_waterLevel )
return;
}
for ( int i = 0 ; i < 3 ; ++i )
{
if ( !pts[i]->used )
{
pts[i]->pos = m_usedPoints++;
pts[i]->used = true;
}
}
addLine( pts[0], pts[1] );
addLine( pts[1], pts[2] );
addLine( pts[2], pts[0] );
}
}
void PMHeightFieldROAM::addLine( pointStructure* pts1, pointStructure* pts2 )
{
for ( int i = 0 ; i < 8 ; ++i )
{
if ( pts1->lines[i] )
{
if ( pts1->lines[i] == pts2 ) return;
}
else
{
for ( int j = 0 ; pts2->lines[j] ; ++j )
{
if ( pts2->lines[j] == pts1 ) return;
}
pts1->lines[i] = pts2;
m_numLines++;
return;
}
}
}
bool PMHeightFieldROAM::createPoints( )
{
m_numPoints = m_size * m_size;
m_pPoints = new( std::nothrow ) pointStructure[ m_numPoints ];
if ( !m_pPoints )
return false;
else
{
clearPoints( true );
return true;
}
}
void PMHeightFieldROAM::clearPoints( bool all )
{
int i, j;
for ( i = 0 ; i < m_numPoints ; ++i )
{
if ( all )
{
m_pPoints[i].hgt = 0;
m_pPoints[i].pos = 0;
}
for ( j = 0 ; j < 8 ; ++j )
m_pPoints[i].lines[j] = 0;
m_pPoints[i].used = false;
}
m_usedPoints = m_numLines = 0;
}
bool PMHeightFieldROAM::createNodes( )
{
m_numNodes = ( ( m_size - 1 ) * ( 4 * ( m_size - 1 ) ) ) - 2;
m_pTree = new( std::nothrow ) triNodeStructure[ m_numNodes ];
if ( !m_pTree )
return false;
else
{
clearNodes( true );
return true;
}
}
void PMHeightFieldROAM::clearNodes( bool all )
{
m_pNextNode = m_pTree;
for ( int i = 0; i < m_numNodes; ++i )
{
if ( all )
{
m_pNextNode->lchd = 0;
m_pNextNode->rchd = 0;
m_pNextNode->base = 0;
m_pNextNode->lnbr = 0;
m_pNextNode->rnbr = 0;
m_pNextNode->vari = 0;
}
m_pNextNode->split = false;
m_pNextNode++;
}
}