/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** 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 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DRoadBuffer.cpp ////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: W3DRoadBuffer.cpp
//
// Created: John Ahlquist, May 2001
//
// Desc: Draw buffer to handle all the roads in a scene.
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include "W3DDevice/GameClient/W3DRoadBuffer.h"
#include
#include
#include
#include
#include "common/GlobalData.h"
#include "common/RandomValue.h"
//#include "Common/GameFileSystem.h"
#include "Common/FileSystem.h" // for LOAD_TEST_ASSETS
#include "GameClient/TerrainRoads.h"
#include "W3DDevice/GameClient/TerrainTex.h"
#include "W3DDevice/GameClient/HeightMap.h"
#include "W3DDevice/GameClient/W3DAssetManager.h"
#include "W3DDevice/GameClient/W3DDynamicLight.h"
#include "W3DDevice/GameClient/WorldHeightMap.h"
#include "W3DDevice/GameClient/W3DShaderManager.h"
#include "WW3D2/Camera.h"
#include "WW3D2/DX8Wrapper.h"
#include "WW3D2/DX8Renderer.h"
#include "WW3D2/Mesh.h"
#include "WW3D2/MeshMdl.h"
static const Real TEE_WIDTH_ADJUSTMENT = 1.03f;
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-----------------------------------------------------------------------------
// Private Data
//-----------------------------------------------------------------------------
// A W3D shader that does alpha, texturing, tests zbuffer, doesn't update zbuffer.
#define SC_ALPHA_DETAIL ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_DISABLE, ShaderClass::COLOR_WRITE_ENABLE, ShaderClass::SRCBLEND_SRC_ALPHA, \
ShaderClass::DSTBLEND_ONE_MINUS_SRC_ALPHA, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE, ShaderClass::SECONDARY_GRADIENT_DISABLE, ShaderClass::TEXTURING_ENABLE, \
ShaderClass::ALPHATEST_DISABLE, ShaderClass::CULL_MODE_ENABLE, \
ShaderClass::DETAILCOLOR_SCALE, ShaderClass::DETAILALPHA_DISABLE) )
static ShaderClass detailAlphaShader(SC_ALPHA_DETAIL);
#define SC_ALPHA_MIRROR ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_DISABLE, ShaderClass::COLOR_WRITE_ENABLE, ShaderClass::SRCBLEND_ONE, \
ShaderClass::DSTBLEND_ZERO, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE, ShaderClass::SECONDARY_GRADIENT_DISABLE, ShaderClass::TEXTURING_ENABLE, \
ShaderClass::ALPHATEST_DISABLE, ShaderClass::CULL_MODE_DISABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
static ShaderClass detailShader(SC_ALPHA_MIRROR);
// The radius of the center of the road is 1.5 - outer radius 2.0, inner radius 1.0.
static const Real CORNER_RADIUS = 1.5f;
// The radius of the center of the road is 0.5 - outer radius 1.0, inner radius 0.0.
static const Real TIGHT_CORNER_RADIUS = 0.5f;
/*
// ShaderClass::PASS_ALWAYS,
#define SC_ALPHA_2D ( SHADE_CNST(PASS_ALWAYS, DEPTH_WRITE_DISABLE, COLOR_WRITE_ENABLE, \
SRCBLEND_SRC_ALPHA, DSTBLEND_ONE_MINUS_SRC_ALPHA, FOG_DISABLE, GRADIENT_DISABLE, \
SECONDARY_GRADIENT_DISABLE, TEXTURING_ENABLE, DETAILCOLOR_DISABLE, DETAILALPHA_DISABLE, \
ALPHATEST_DISABLE, CULL_MODE_ENABLE, DETAILCOLOR_DISABLE, DE AILALPHA_DISABLE) )
ShaderClass ShaderClass::_PresetAlpha2DShader(SC_ALPHA_2D);
*/
/** Calculate the sign of the cross product. If the tails of the vectors are both placed
at 0,0, then the cross product can be interpreted as -1 means v2 is to the right of v1,
1 means v2 is to the left of v1, and 0 means v2 is parallel to v1. */
static Int xpSign(const Vector2 &v1, const Vector2 &v2) {
Real xpdct = v1.X*v2.Y - v1.Y*v2.X;
if (xpdct<0) return -1;
if (xpdct>0) return 1;
return 0;
}
//-----------------------------------------------------------------------------
// Private Class
//-----------------------------------------------------------------------------
//=============================================================================
// RoadType constructor
//=============================================================================
/** Nulls index & vertex data. */
//=============================================================================
RoadType::RoadType(void):
m_roadTexture(NULL),
m_vertexRoad(NULL),
m_indexRoad(NULL),
m_stackingOrder(0),
m_uniqueID(-1)
{
}
//=============================================================================
// RoadType destructor
//=============================================================================
/** Frees index & vertex data. */
//=============================================================================
RoadType::~RoadType(void)
{
REF_PTR_RELEASE(m_roadTexture);
REF_PTR_RELEASE(m_vertexRoad);
REF_PTR_RELEASE(m_indexRoad);
}
//=============================================================================
// RoadType applyTexture
//=============================================================================
/** Sets the W3D texture. */
//=============================================================================
void RoadType::applyTexture(void)
{
W3DShaderManager::setTexture(0,m_roadTexture);
DX8Wrapper::Set_Index_Buffer(m_indexRoad,0);
DX8Wrapper::Set_Vertex_Buffer(m_vertexRoad);
}
//=============================================================================
// RoadType loadTexture
//=============================================================================
/** Sets the W3D texture. */
//=============================================================================
void RoadType::loadTexture(AsciiString path, Int ID)
{
/// @todo - delay loading textures and only load textures referenced by map.
WW3DAssetManager *pMgr = W3DAssetManager::Get_Instance();
m_roadTexture = pMgr->Get_Texture(path.str(), TextureClass::MIP_LEVELS_3);
m_roadTexture->Set_Mip_Mapping( TextureClass::FILTER_TYPE_BEST );
m_roadTexture->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_REPEAT);
m_roadTexture->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_REPEAT);
m_vertexRoad=NEW_REF(DX8VertexBufferClass,(DX8_FVF_XYZDUV1,TheGlobalData->m_maxRoadVertex+4,DX8VertexBufferClass::USAGE_DYNAMIC));
m_indexRoad=NEW_REF(DX8IndexBufferClass,(TheGlobalData->m_maxRoadIndex+4, DX8IndexBufferClass::USAGE_DYNAMIC));
m_numRoadVertices=0;
m_numRoadIndices=0;
#ifdef LOAD_TEST_ASSETS
m_texturePath = path;
#endif
m_uniqueID = ID;
}
#ifdef LOAD_TEST_ASSETS
//=============================================================================
// RoadType loadTexture
//=============================================================================
/** Sets the W3D texture. */
//=============================================================================
void RoadType::loadTestTexture(void)
{
if (m_isAutoLoaded && m_uniqueID>0 && !m_texturePath.isEmpty()) {
/// @todo - delay loading textures and only load textures referenced by map.
m_roadTexture = NEW_REF(TextureClass, (m_texturePath.str(), m_texturePath.str(), TextureClass::MIP_LEVELS_3));
m_roadTexture->Set_Mip_Mapping( TextureClass::FILTER_TYPE_BEST );
m_roadTexture->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_REPEAT);
m_roadTexture->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_REPEAT);
}
}
#endif
//=============================================================================
// RoadSegment constructor
//=============================================================================
/** Nulls index & vertex data. */
//=============================================================================
RoadSegment::RoadSegment(void)
{
m_numVertex = 0;
m_vb = NULL;
m_numIndex = 0;
m_ib = NULL;
}
//=============================================================================
// RoadSegment destructor
//=============================================================================
/** Frees index & vertex data. */
//=============================================================================
RoadSegment::~RoadSegment(void)
{
m_numVertex = 0;
if (m_vb) {
delete m_vb;
}
m_vb= NULL;
m_numIndex = 0;
if (m_ib) {
delete m_ib;
}
m_ib = NULL;
}
//=============================================================================
// RoadSegment::SetVertexBuffer
//=============================================================================
/** Allocates & sets the vertex entries. */
//=============================================================================
void RoadSegment::SetVertexBuffer(VertexFormatXYZDUV1 *vb, Int numVertex)
{
if (m_vb) {
delete m_vb;
m_vb = NULL;
m_numVertex = 0;
}
Vector3 verts[MAX_SEG_VERTEX];
if (numVertex<1 || numVertex > MAX_SEG_VERTEX) return;
m_vb = NEW VertexFormatXYZDUV1[numVertex]; // pool[]ify
if (!m_vb) return;
m_numVertex = numVertex;
memcpy(m_vb, vb, numVertex*sizeof(VertexFormatXYZDUV1));
Int i;
for (i=0; i MAX_SEG_INDEX) return;
m_ib = NEW UnsignedShort[numIndex];
if (!m_ib) return;
m_numIndex = numIndex;
memcpy(m_ib, ib, numIndex*sizeof(UnsignedShort));
}
//=============================================================================
// RoadSegment::GetVertices
//=============================================================================
/** Copies vertex entries into destination_vb. */
//=============================================================================
Int RoadSegment::GetVertices(VertexFormatXYZDUV1 *destination_vb, Int numToCopy)
{
if (m_vb == NULL || numToCopy<1) return (0);
if (numToCopy > m_numVertex) return(0);
memcpy(destination_vb, m_vb, numToCopy*sizeof(VertexFormatXYZDUV1));
return(numToCopy);
}
//=============================================================================
// RoadSegment::GetIndices
//=============================================================================
/** Copies index entries into destination_ib. */
//=============================================================================
Int RoadSegment::GetIndices(UnsignedShort *destination_ib, Int numToCopy, Int offset)
{
if (m_ib == NULL || numToCopy<1) return (0);
if (numToCopy > m_numIndex) return(0);
Int i;
for (i=0; igetMap()->getBorderSize();
y += TheTerrainRenderObject->getMap()->getBorderSize();
m_vb[i].diffuse = (255<<24)|TheTerrainRenderObject->getStaticDiffuse(x, y);
}
}
//-----------------------------------------------------------------------------
// Private Methods
//-----------------------------------------------------------------------------
//=============================================================================
// W3DRoadBuffer::loadTee
//=============================================================================
/** Loads a tee into the vertex buffer for a road join. */
//=============================================================================
void W3DRoadBuffer::loadTee(RoadSegment *pRoad, Vector2 loc1,
Vector2 loc2, Bool is4Way, Real scale)
{
Vector2 roadVector(loc2.X-loc1.X, loc2.Y-loc1.Y);
Vector2 roadNormal(-roadVector.Y, roadVector.X);
// Real roadLen = scale;
if (abs(roadVector.X) < MIN_ROAD_SEGMENT && abs(roadVector.Y) < MIN_ROAD_SEGMENT) {
roadVector.Set(1.0f, 0.0f);
roadNormal.Set(0.0f, 1.0f);
} else {
roadVector.Normalize();
roadNormal.Normalize();
}
Real uOffset = (425.0f/512.0f);
Real vOffset = (255.0f/512.0f);
if (is4Way) {
uOffset = (425.0f/512.0f);
vOffset = (425.0f/512.0f);
}
Real teeFactor = scale*TEE_WIDTH_ADJUSTMENT/2.0f;
Real left = pRoad->m_widthInTexture*scale/2.0f;
loadFloatSection(pRoad, loc1, loc2-loc1, teeFactor, left, teeFactor, uOffset, vOffset, scale);
}
//=============================================================================
// W3DRoadBuffer::loadAlphaJoin
//=============================================================================
/** Loads an alpha join into the vertex buffer for a road join. */
//=============================================================================
void W3DRoadBuffer::loadAlphaJoin(RoadSegment *pRoad, Vector2 loc1,
Vector2 loc2,Real scale)
{
Vector2 roadVector(loc2.X-loc1.X, loc2.Y-loc1.Y);
Vector2 roadNormal(-roadVector.Y, roadVector.X);
// Real roadLen = scale;
if (abs(roadVector.X) < MIN_ROAD_SEGMENT && abs(roadVector.Y) < MIN_ROAD_SEGMENT) {
roadVector.Set(1.0f, 0.0f);
roadNormal.Set(0.0f, 1.0f);
} else {
roadVector.Normalize();
roadNormal.Normalize();
}
Real uOffset = (106.0f/512.0f);
Real vOffset = (425.0f/512.0f);
Real roadwidth = scale;
Real uScale = pRoad->m_widthInTexture;
roadVector *= roadwidth*48/128; // we just want 48 pixels.
roadNormal *= uScale*(1+8.0f/128); // we want 8 extra pixels.
Vector2 corners[NUM_CORNERS];
corners[topLeft] = loc1 + roadNormal*0.5f - roadVector*0.65f ;
corners[bottomLeft] = corners[topLeft] - roadNormal;
corners[bottomRight] = corners[bottomLeft] + roadVector;
corners[topRight] = corners[topLeft] + roadVector;
loadFloat4PtSection(pRoad, loc1, roadNormal, roadVector, corners, uOffset, vOffset, scale, uScale);
}
//=============================================================================
// W3DRoadBuffer::loadY
//=============================================================================
/** Loads a Y into the vertex buffer for a road join. */
//=============================================================================
void W3DRoadBuffer::loadY(RoadSegment *pRoad, Vector2 loc1,
Vector2 loc2,Real scale)
{
Vector2 roadVector(loc2.X-loc1.X, loc2.Y-loc1.Y);
Vector2 roadNormal(-roadVector.Y, roadVector.X);
// Real roadLen = scale;
if (abs(roadVector.X) < MIN_ROAD_SEGMENT && abs(roadVector.Y) < MIN_ROAD_SEGMENT) {
roadVector.Set(1.0f, 0.0f);
roadNormal.Set(0.0f, 1.0f);
} else {
roadVector.Normalize();
roadNormal.Normalize();
}
Real uOffset = (255.0f/512.0f);
Real vOffset = (226.0f/512.0f);
Real roadwidth = scale;
roadVector *= roadwidth;
roadNormal *= roadwidth;
Vector2 corners[NUM_CORNERS];
roadVector *= 1.59f;
corners[topLeft] = loc1 + roadNormal*0.29f - roadVector*0.5f ;
corners[bottomLeft] = corners[topLeft] - roadNormal*1.08f;
corners[bottomRight] = corners[bottomLeft] + roadVector;
corners[topRight] = corners[topLeft] + roadVector;
loadFloat4PtSection(pRoad, loc1, roadNormal, roadVector, corners, uOffset, vOffset, scale, scale);
}
//=============================================================================
// W3DRoadBuffer::loadH
//=============================================================================
/** Loads a h shaped tee into the vertex buffer for a road join. */
//=============================================================================
void W3DRoadBuffer::loadH(RoadSegment *pRoad, Vector2 loc1,
Vector2 loc2, Bool flip, Real scale)
{
Vector2 roadVector(loc2.X-loc1.X, loc2.Y-loc1.Y);
Vector2 roadNormal(-roadVector.Y, roadVector.X);
// Real roadLen = scale;
if (abs(roadVector.X) < MIN_ROAD_SEGMENT && abs(roadVector.Y) < MIN_ROAD_SEGMENT) {
roadVector.Set(1.0f, 0.0f);
roadNormal.Set(0.0f, 1.0f);
} else {
roadVector.Normalize();
roadNormal.Normalize();
}
Real uOffset = (202.0f/512.0f);
Real vOffset = (364.0f/512.0f);
Real roadwidth = scale;
roadVector *= roadwidth;
roadNormal *= roadwidth;
Vector2 corners[NUM_CORNERS];
roadNormal *= 1.35f;
if (flip) {
corners[bottomLeft] = loc1 - roadNormal*0.20f - roadVector*pRoad->m_widthInTexture/2;
} else {
corners[bottomLeft] = loc1 - roadNormal*0.8f - roadVector*pRoad->m_widthInTexture/2;
}
Vector2 width = roadVector*pRoad->m_widthInTexture/2;
width = width + roadVector * 1.2f;
corners[bottomRight] = corners[bottomLeft] + width;
corners[topRight] = corners[bottomRight] + roadNormal;
corners[topLeft] = corners[bottomLeft] + roadNormal;
if (flip) roadNormal = -roadNormal;
loadFloat4PtSection(pRoad, loc1, roadNormal, roadVector, corners, uOffset, vOffset, scale, scale);
}
//=============================================================================
// W3DRoadBuffer::loadFloatSection
//=============================================================================
/** Loads a section of road using a mesh that floats a little above the
terrain. */
//=============================================================================
void W3DRoadBuffer::loadFloatSection(RoadSegment *pRoad, Vector2 loc,
Vector2 roadVector, Real halfHeight, Real left, Real right,
Real uOffset, Real vOffset, Real scale)
{
if (m_map==NULL) {
return;
}
Vector2 roadNormal(-roadVector.Y, roadVector.X);
roadVector.Normalize();
roadVector *= right;
roadNormal.Normalize();
if (halfHeight<0) halfHeight = -halfHeight;
roadNormal *= halfHeight;
Vector2 roadLeft = roadVector;
roadLeft.Normalize();
roadLeft *= left;
roadVector += roadLeft;
Vector2 leftCenter = loc;
leftCenter -= roadLeft;
Vector2 corners[NUM_CORNERS];
corners[bottomLeft] = leftCenter - roadNormal;
corners[bottomRight] = corners[bottomLeft];
corners[bottomRight] += roadVector;
corners[topRight] = corners[bottomRight];
corners[topRight] += 2*roadNormal;
corners[topLeft] = corners[bottomLeft];
corners[topLeft] += 2*roadNormal;
loadFloat4PtSection(pRoad, loc, roadNormal, roadVector, corners, uOffset, vOffset, scale, scale);
}
//=============================================================================
// W3DRoadBuffer::loadFloat4PtSection
//=============================================================================
/** Loads a section of road using a mesh that floats a little above the
terrain. The road is loaded into the quadrilateral defined by the
4 corners points. loc specifies the point where u==uOffset && v==vOffset, and
the road vector gives the direction of the road, and the road normal is perpendicular
to the road normal. */
//=============================================================================
void W3DRoadBuffer::loadFloat4PtSection(RoadSegment *pRoad, Vector2 loc,
Vector2 roadNormal, Vector2 roadVector,
Vector2 *cornersP,
Real uOffset, Real vOffset, Real uScale, Real vScale)
{
const Real FLOAT_AMOUNT = MAP_HEIGHT_SCALE/8;
const Real MAX_ERROR = MAP_HEIGHT_SCALE*1.1f;
UnsignedShort ib[MAX_SEG_INDEX];
VertexFormatXYZDUV1 vb[MAX_SEG_VERTEX];
Int numRoadVertices = 0;
Int numRoadIndices = 0;
TRoadSegInfo info;
info.loc = loc;
info.roadNormal = roadNormal;
info.roadVector = roadVector;
info.corners[bottomLeft] = cornersP[bottomLeft];
info.corners[bottomRight] = cornersP[bottomRight];
info.corners[topLeft] = cornersP[topLeft];
info.corners[topRight] = cornersP[topRight];
info.uOffset = uOffset;
info.vOffset = vOffset;
info.scale = uScale;
pRoad->SetRoadSegInfo(&info);
Real roadLen = roadVector.Length();
Real halfHeight = roadNormal.Length();
roadNormal.Normalize();
roadVector.Normalize();
Vector2 curVector;
Int uCount = (roadLen/MAP_XY_FACTOR)+1;
if (uCount<2) uCount = 2;
Int vCount = (2*halfHeight/MAP_XY_FACTOR)+1;
if (vCount<2) vCount = 2;
const int maxRows = 100;
typedef struct {
Bool collapsed;
Bool deleted;
Vector3 vtx[maxRows];
Int diffuseRed;
Bool lightGradient;
Int vertexIndex[maxRows];
Real uIndex;
} TColumn;
const Int DIFFUSE_LIMIT = 25; // if more than that, we tesselate :) jba.
if (vCount>maxRows) vCount = maxRows;
TColumn prevColumn, curColumn, nextColumn;
prevColumn.deleted = true;
curColumn.deleted = true;
Int i, j, k;
Vector2 v2 = cornersP[bottomLeft];
Vector3 origin(v2.X, v2.Y, 0);
v2 = cornersP[bottomRight] - cornersP[bottomLeft];
Vector3 uVector1(v2.X, v2.Y, 0);
v2 = cornersP[topRight] - cornersP[topLeft];
Vector3 uVector2(v2.X, v2.Y, 0);
v2 = cornersP[topLeft];
Vector3 origin2(v2.X, v2.Y, 0);
v2 = cornersP[topLeft] - cornersP[bottomLeft];
Vector3 vVector1(v2.X, v2.Y, 0);
v2 = cornersP[topRight] - cornersP[bottomRight];
Vector3 vVector2(v2.X, v2.Y, 0);
uVector2 += (vVector1 - vVector2);
for (i=0; i<=uCount; i++) {
Real iFactor = ((Real)i / (uCount-1));
Real iBarFactor = 1.0f-iFactor;
if (igetMaxHeightValue()*MAP_HEIGHT_SCALE;
Real maxHeight = m_map->getMinHeightValue()*MAP_HEIGHT_SCALE;
for (j=0; jgetMaxCellHeight(nextColumn.vtx[j].X, nextColumn.vtx[j].Y);
if (zmaxHeight) maxHeight = z;
nextColumn.vertexIndex[j] = -1;
nextColumn.vtx[j].Z = z;
Int diffuse = 0;
if (j==0) {
nextColumn.diffuseRed = (diffuse & 0x00ff);
} else {
Int red = diffuse&0x00ff;
if (abs(red-nextColumn.diffuseRed) > DIFFUSE_LIMIT) {
nextColumn.lightGradient = true;
}
}
}
if (true) { // !nextColumn.lightGradient) {
nextColumn.collapsed = true;
nextColumn.vtx[0].Z = maxHeight;
nextColumn.vtx[1] = nextColumn.vtx[vCount-1];
nextColumn.vtx[1].Z = maxHeight;
} else {
for (j=0; j= curColumn.vtx[0].Z && theZ < curColumn.vtx[0].Z + MAX_ERROR) {
theZ = prevColumn.vtx[1].Z * (curColumn.uIndex-prevColumn.uIndex) +
nextColumn.vtx[1].Z * (nextColumn.uIndex-curColumn.uIndex);
theZ /= nextColumn.uIndex-prevColumn.uIndex;
if (theZ >= curColumn.vtx[1].Z && theZ < curColumn.vtx[1].Z + MAX_ERROR) {
okToDelete = true;
}
}
if (okToDelete) {
curColumn.deleted = true;
}
}
}
}
if (!curColumn.deleted && i!=1) {
// Write out the vertices.
for (j=0; j= MAX_SEG_INDEX) {
break;
}
curVector.Set(curColumn.vtx[j].X - loc.X, curColumn.vtx[j].Y - loc.Y);
V = Vector2::Dot_Product(roadNormal, curVector);
U = Vector2::Dot_Product(roadVector, curVector);
Int diffuse = 0;
#ifdef _DEBUG
//diffuse &= 0xFFFF00FF; // strip out green.
#endif
vb[numRoadVertices].u1 = uOffset+U/(uScale*4);
vb[numRoadVertices].v1 = vOffset-V/(vScale*4); // Road is 1/16 texture height.
vb[numRoadVertices].x = curColumn.vtx[j].X;
vb[numRoadVertices].y = curColumn.vtx[j].Y;
vb[numRoadVertices].z = curColumn.vtx[j].Z+FLOAT_AMOUNT;
vb[numRoadVertices].diffuse = diffuse;
curColumn.vertexIndex[j] = numRoadVertices;
numRoadVertices++;
if (j==1 && curColumn.collapsed) {
break;
}
}
if (numRoadVertices >= MAX_SEG_INDEX) {
break;
}
if (i>1) {
// Write out the triangles.
j = 0;
k = 0;
while (j= MAX_SEG_INDEX) {
break;
}
UnsignedShort *curIb = ib+numRoadIndices;
if (k==0 || !prevColumn.collapsed) {
*curIb++ = prevColumn.vertexIndex[j+1];
*curIb++ = prevColumn.vertexIndex[j];
*curIb++ = curColumn.vertexIndex[k];
numRoadIndices+=3;
}
if (j==0 || !curColumn.collapsed) {
Int offset = 1;
if (curColumn.collapsed && !prevColumn.collapsed) {
offset = vCount-1;
}
*curIb++ = prevColumn.vertexIndex[j+offset];
*curIb++ = curColumn.vertexIndex[k];
*curIb++ = curColumn.vertexIndex[k+1];
numRoadIndices+=3;
}
if (prevColumn.collapsed && curColumn.collapsed) {
break;
}
if (!prevColumn.collapsed) {
j++;
}
if (!curColumn.collapsed) {
k++;
}
}
prevColumn = curColumn;
} else if (i==0) {
prevColumn = curColumn;
}
if (numRoadIndices >= MAX_SEG_INDEX) {
break;
}
}
curColumn = nextColumn;
}
pRoad->SetVertexBuffer(vb, numRoadVertices);
pRoad->SetIndexBuffer(ib, numRoadIndices);
}
//=============================================================================
// W3DRoadBuffer::loadLit4PtSection
//=============================================================================
/** Loads a section of road using a mesh that floats a little above the
terrain. The road is loaded into the quadrilateral defined by the
4 corners points. loc specifies the point where u==uOffset && v==vOffset, and
the road vector gives the direction of the road, and the road normal is perpendicular
to the road normal. */
//=============================================================================
void W3DRoadBuffer::loadLit4PtSection(RoadSegment *pRoad, UnsignedShort *ib, VertexFormatXYZDUV1 *vb, RefRenderObjListIterator *pDynamicLightsIterator)
{
const Real FLOAT_AMOUNT = MAP_HEIGHT_SCALE/8;
const Real MAX_ERROR = MAP_HEIGHT_SCALE*1.1f;
if (pRoad->m_uniqueID != m_curUniqueID) {
return;
}
// Throw out segs out of view.
if (pRoad->m_pt1.loc.X + pRoad->m_scale/2 < this->m_minX &&
pRoad->m_pt2.loc.X + pRoad->m_scale/2 < this->m_minX) {
return;
}
if (pRoad->m_pt1.loc.X - pRoad->m_scale/2 > this->m_maxX &&
pRoad->m_pt2.loc.X - pRoad->m_scale/2 > this->m_maxX) {
return;
}
// Throw out segs out of view.
if (pRoad->m_pt1.loc.Y + pRoad->m_scale/2 < this->m_minY &&
pRoad->m_pt2.loc.Y + pRoad->m_scale/2 < this->m_minY) {
return;
}
if (pRoad->m_pt1.loc.Y - pRoad->m_scale/2 > this->m_maxY &&
pRoad->m_pt2.loc.Y - pRoad->m_scale/2 > this->m_maxY) {
return;
}
Int numLights = 0;
const Int maxLights = 8;
LightClass *lights[maxLights];
for (pDynamicLightsIterator->First(); !pDynamicLightsIterator->Is_Done(); pDynamicLightsIterator->Next()) {
LightClass *pLight = (LightClass*)pDynamicLightsIterator->Peek_Obj();
SphereClass bounds = pLight->Get_Bounding_Sphere();
if (Spheres_Intersect(pRoad->getBounds(), bounds)) {
lights[numLights] = pLight;
numLights++;
if (numLights == maxLights) break;
}
}
if (numLights == 0) return;
TRoadSegInfo info;
pRoad->GetRoadSegInfo(&info);
Real roadLen = info.roadVector.Length();
Real halfHeight = info.roadNormal.Length();
info.roadNormal.Normalize();
info.roadVector.Normalize();
Vector2 curVector;
Int uCount = (roadLen/MAP_XY_FACTOR)+1;
Int vCount = (2*halfHeight/MAP_XY_FACTOR)+1;
const int maxRows = 100;
typedef struct {
Bool collapsed;
Bool deleted;
Vector3 vtx[maxRows];
Int diffuseRed;
Bool lightGradient;
Int vertexIndex[maxRows];
Real uIndex;
} TColumn;
// const Int DIFFUSE_LIMIT = 25; // if more than that, we tesselate :) jba.
if (vCount>maxRows) vCount = maxRows;
TColumn prevColumn, curColumn, nextColumn;
prevColumn.deleted = true;
curColumn.deleted = true;
Int i, j, k;
Vector2 v2 = info.corners[bottomLeft];
Vector3 origin(v2.X, v2.Y, 0);
v2 = info.corners[bottomRight] - info.corners[bottomLeft];
Vector3 uVector1(v2.X, v2.Y, 0);
v2 = info.corners[topRight] - info.corners[topLeft];
Vector3 uVector2(v2.X, v2.Y, 0);
v2 = info.corners[topLeft];
Vector3 origin2(v2.X, v2.Y, 0);
v2 = info.corners[topLeft] - info.corners[bottomLeft];
Vector3 vVector1(v2.X, v2.Y, 0);
v2 = info.corners[topRight] - info.corners[bottomRight];
Vector3 vVector2(v2.X, v2.Y, 0);
uVector2 += (vVector1 - vVector2);
for (i=0; i<=uCount; i++) {
Real iFactor = ((Real)i / (uCount-1));
Real iBarFactor = 1.0f-iFactor;
if (igetMaxHeightValue()*MAP_HEIGHT_SCALE;
Real maxHeight = m_map->getMinHeightValue()*MAP_HEIGHT_SCALE;
for (j=0; jgetMaxCellHeight(nextColumn.vtx[j].X, nextColumn.vtx[j].Y);
if (zmaxHeight) maxHeight = z;
nextColumn.vertexIndex[j] = -1;
nextColumn.vtx[j].Z = z;
Int k;
for (k=0; kGet_Position();
Real range = lights[k]->Get_Attenuation_Range();
// for culling, expand one cell radius.
range += MAP_XY_FACTOR;
if (offset.Length2() < range*range) {
nextColumn.lightGradient = true;
}
}
}
if (!nextColumn.lightGradient) {
nextColumn.collapsed = true;
nextColumn.vtx[0].Z = maxHeight;
nextColumn.vtx[1] = nextColumn.vtx[vCount-1];
nextColumn.vtx[1].Z = maxHeight;
} else {
for (j=0; j= curColumn.vtx[0].Z && theZ < curColumn.vtx[0].Z + MAX_ERROR) {
theZ = prevColumn.vtx[1].Z * (curColumn.uIndex-prevColumn.uIndex) +
nextColumn.vtx[1].Z * (nextColumn.uIndex-curColumn.uIndex);
theZ /= nextColumn.uIndex-prevColumn.uIndex;
if (theZ >= curColumn.vtx[1].Z && theZ < curColumn.vtx[1].Z + MAX_ERROR) {
okToDelete = true;
}
}
if (okToDelete) {
curColumn.deleted = true;
}
}
}
}
if (!curColumn.deleted && i!=1) {
// Write out the vertices.
for (j=0; j= m_maxRoadVertex) {
break;
}
curVector.Set(curColumn.vtx[j].X - info.loc.X, curColumn.vtx[j].Y - info.loc.Y);
V = Vector2::Dot_Product(info.roadNormal, curVector);
U = Vector2::Dot_Product(info.roadVector, curVector);
Int diffuse = (255<<24)|TheTerrainRenderObject->getStaticDiffuse(curColumn.vtx[j].X/MAP_XY_FACTOR+0.5, curColumn.vtx[j].Y/MAP_XY_FACTOR+0.5);
Real shadeR, shadeG, shadeB;
shadeB = (diffuse & 0xFF)/255.0;
shadeG = ((diffuse>>8) & 0xFF)/255.0;
shadeR = ((diffuse>>16) & 0xFF)/255.0;
Int k;
for (k=0; kGet_Type() == LightClass::POINT) {
Vector3 lightLoc = lights[k]->Get_Position();
Vector3 vtx = curColumn.vtx[j];
Vector3 offset = vtx - lightLoc;
double range, midRange;
lights[k]->Get_Far_Attenuation_Range(midRange, range);
if (vtx.X < lightLoc.X-range) continue;
if (vtx.X > lightLoc.X+range) continue;
if (vtx.Y < lightLoc.Y-range) continue;
if (vtx.Y > lightLoc.Y+range) continue;
Real dist = offset.Length();
if (dist >= range) continue;
if (midRange < 0.1) continue;
#if 1
factor = 1.0f - (dist - midRange) / (range - midRange);
#else
// f = 1.0 / (atten0 + d*atten1 + d*d/atten2);
if (fabs(range-midRange)<1e-5) {
// if the attenuation range is too small assume uniform with cutoff
factor = 1.0;
} else {
factor = 1.0f/(0.1+dist/midRange + 5.0f*dist*dist/(range*range));
}
#endif
factor = WWMath::Clamp(factor,0.0f,1.0f);
Real shade = 0.5f;
shade *= factor;
Vector3 diffuse;
lights[k]->Get_Diffuse(&diffuse);
Vector3 ambient;
lights[k]->Get_Ambient(&ambient);
if (shade > 1.0) shade = 1.0;
if(shade < 0.0f) shade = 0.0f;
shadeR += shade*diffuse.X;
shadeG += shade*diffuse.Y;
shadeB += shade*diffuse.Z;
shadeR += factor*ambient.X;
shadeG += factor*ambient.Y;
shadeB += factor*ambient.Z;
}
}
if (shadeR > 1.0) shadeR = 1.0;
if(shadeR < 0.0f) shadeR = 0.0f;
if (shadeG > 1.0) shadeG = 1.0;
if(shadeG < 0.0f) shadeG = 0.0f;
if (shadeB > 1.0) shadeB = 1.0;
if(shadeB < 0.0f) shadeB = 0.0f;
shadeR*=255;
shadeG*=255;
shadeB*=255;
diffuse=REAL_TO_INT(shadeB) | (REAL_TO_INT(shadeG) << 8) | (REAL_TO_INT(shadeR) << 16) | ((int)255 << 24);
#ifdef _DEBUG
//diffuse &= 0xFFFF00FF; // strip out green.
#endif
vb[m_curNumRoadVertices].u1 = info.uOffset+U/(info.scale*4);
vb[m_curNumRoadVertices].v1 = info.vOffset-V/(info.scale*4); // Road is 1/16 texture height.
vb[m_curNumRoadVertices].x = curColumn.vtx[j].X;
vb[m_curNumRoadVertices].y = curColumn.vtx[j].Y;
vb[m_curNumRoadVertices].z = curColumn.vtx[j].Z+FLOAT_AMOUNT;
vb[m_curNumRoadVertices].diffuse = diffuse;
curColumn.vertexIndex[j] = m_curNumRoadVertices;
m_curNumRoadVertices++;
if (j==1 && curColumn.collapsed) {
break;
}
}
if (m_curNumRoadVertices >= MAX_SEG_INDEX) {
break;
}
if (i>1 && (!prevColumn.collapsed || !curColumn.collapsed)) {
// Write out the triangles.
j = 0;
k = 0;
while (j= m_maxRoadIndex) {
break;
}
UnsignedShort *curIb = ib+m_curNumRoadIndices;
if (k==0 || !prevColumn.collapsed) {
*curIb++ = prevColumn.vertexIndex[j+1];
*curIb++ = prevColumn.vertexIndex[j];
*curIb++ = curColumn.vertexIndex[k];
m_curNumRoadIndices+=3;
}
if (j==0 || !curColumn.collapsed) {
Int offset = 1;
if (curColumn.collapsed && !prevColumn.collapsed) {
offset = vCount-1;
}
*curIb++ = prevColumn.vertexIndex[j+offset];
*curIb++ = curColumn.vertexIndex[k];
*curIb++ = curColumn.vertexIndex[k+1];
m_curNumRoadIndices+=3;
}
if (prevColumn.collapsed && curColumn.collapsed) {
break;
}
if (!prevColumn.collapsed) {
j++;
}
if (!curColumn.collapsed) {
k++;
}
}
prevColumn = curColumn;
} else if (i==0) {
prevColumn = curColumn;
}
if (m_curNumRoadIndices >= MAX_SEG_INDEX) {
break;
}
}
curColumn = nextColumn;
}
}
//=============================================================================
// W3DRoadBuffer::loadCurve
//=============================================================================
/** Loads a curve segment into the vertex buffer for a road end cap or join. */
//=============================================================================
void W3DRoadBuffer::loadCurve(RoadSegment *pRoad, Vector2 loc1, Vector2 loc2, Real scale)
{
Real uOffset = (4.0f/512.0f);
Real vOffset = (255.0f/512.0f); // CORNER_RADIUS radius 1.5 curve.
if (pRoad->m_curveRadius == TIGHT_CORNER_RADIUS) {
vOffset = (425.0f/512.0f);
}
Vector2 roadVector(loc2.X-loc1.X, loc2.Y-loc1.Y);
Vector2 roadNormal(-roadVector.Y, roadVector.X);
Real roadLen = scale;
Real curveHeight = pRoad->m_widthInTexture*scale/2.0f;
roadVector.Normalize();
roadVector *= roadLen;
roadNormal.Normalize();
roadNormal *= abs(curveHeight);
Vector2 corners[NUM_CORNERS];
corners[bottomLeft] = loc1 - roadNormal;
corners[bottomRight] = corners[bottomLeft];
corners[bottomRight] += roadVector;
corners[topRight] = corners[bottomRight];
corners[topRight] += 2*roadNormal;
corners[topLeft] = corners[bottomLeft];
corners[topLeft] += 2*roadNormal;
// Tweak a little
if (pRoad->m_curveRadius == TIGHT_CORNER_RADIUS) {
corners[bottomLeft] = loc1 - roadNormal;
corners[bottomRight] = corners[bottomLeft];
corners[bottomRight] += roadVector*0.5f;
corners[topRight] = corners[bottomRight];
corners[topRight] += 2*roadNormal;
corners[topLeft] = corners[bottomLeft];
corners[topLeft] += 2*roadNormal;
corners[bottomRight] += roadVector*0.1f;
corners[bottomRight] += roadNormal*0.2f;
corners[bottomLeft] -= roadNormal*0.1f;
corners[bottomLeft] -= roadVector*0.02f;
corners[topLeft] -= roadVector*0.02f;
corners[topRight] -= roadVector*0.4f;
corners[topRight] += roadNormal*0.2f;
} else {
corners[bottomLeft] = loc1 - roadNormal;
corners[bottomRight] = corners[bottomLeft];
corners[bottomRight] += roadVector;
corners[topRight] = corners[bottomRight];
corners[topRight] += 2*roadNormal;
corners[topLeft] = corners[bottomLeft];
corners[topLeft] += 2*roadNormal;
corners[bottomRight] += roadVector*0.1f;
corners[bottomRight] += roadNormal*0.4f;
corners[bottomLeft] -= roadNormal*0.2f;
corners[bottomLeft] -= roadVector*0.02f;
corners[topLeft] -= roadVector*0.02f;
corners[topRight] -= roadVector*0.4f;
corners[topRight] += roadNormal*0.4f;
}
loadFloat4PtSection(pRoad, loc1, roadNormal, roadVector, corners, uOffset, vOffset, scale, scale);
}
//=============================================================================
// W3DRoadBuffer::preloadRoadSegment
//=============================================================================
/** Loads a road segment into the vertex buffer for drawing. */
//=============================================================================
void W3DRoadBuffer::preloadRoadSegment(RoadSegment *pRoad)
{
Vector2 roadVector = pRoad->m_pt2.loc-pRoad->m_pt1.loc;
Vector2 roadNormal(-roadVector.Y, roadVector.X);
// Real roadLen = roadVector.Length();
Real uOffset = 0.0f;
Real vOffset = (85.0f/512.0f);
Real roadHeight = pRoad->m_widthInTexture*pRoad->m_scale/2.0f;
roadNormal.Normalize();
roadNormal *= roadHeight;
Vector2 corners[NUM_CORNERS];
corners[bottomLeft] = pRoad->m_pt1.bottom;
corners[topLeft] = pRoad->m_pt1.top;
corners[bottomRight] = pRoad->m_pt2.bottom;
corners[topRight] = pRoad->m_pt2.top;
loadFloat4PtSection(pRoad, pRoad->m_pt1.loc, roadNormal, roadVector, corners, uOffset, vOffset,
pRoad->m_scale, pRoad->m_scale);
}
//=============================================================================
// W3DRoadBuffer::preloadRoadsInVertexAndIndexBuffers
//=============================================================================
/** Loads the roads into the vertex buffer for drawing. */
//=============================================================================
void W3DRoadBuffer::preloadRoadsInVertexAndIndexBuffers()
{
if (!m_initialized) {
return;
}
Int curRoad;
// Do road segments.
for (curRoad=0; curRoadm_roadTypes[m_curRoadType].setNumVertices(0);
this->m_roadTypes[m_curRoadType].setNumIndices(0);
return;
}
DX8IndexBufferClass::WriteLockClass lockIdxBuffer(m_roadTypes[m_curRoadType].getIB());
DX8VertexBufferClass::WriteLockClass lockVtxBuffer(m_roadTypes[m_curRoadType].getVB());
vb=(VertexFormatXYZDUV1*)lockVtxBuffer.Get_Vertex_Array();
ib = lockIdxBuffer.Get_Index_Array();
// Add to the index buffer & vertex buffer.
Int curRoad;
// Do road segments.
TCorner corner;
for (corner = SEGMENT; corner < NUM_JOINS; corner = (TCorner)(corner+1)) {
for (curRoad=0; curRoadm_roadTypes[m_curRoadType].setNumVertices(m_curNumRoadVertices);
this->m_roadTypes[m_curRoadType].setNumIndices(m_curNumRoadIndices);
}
//=============================================================================
// W3DRoadBuffer::loadLitRoadsInVertexAndIndexBuffers
//=============================================================================
/** Loads the roads into the vertex buffer for drawing. */
//=============================================================================
void W3DRoadBuffer::loadLitRoadsInVertexAndIndexBuffers(RefRenderObjListIterator *pDynamicLightsIterator)
{
if ( !m_initialized) {
return;
}
m_curNumRoadVertices = 0;
m_curNumRoadIndices = 0;
VertexFormatXYZDUV1 *vb;
UnsignedShort *ib;
// Lock the buffers.
DX8IndexBufferClass::WriteLockClass lockIdxBuffer(m_roadTypes[m_curRoadType].getIB());
DX8VertexBufferClass::WriteLockClass lockVtxBuffer(m_roadTypes[m_curRoadType].getVB());
vb=(VertexFormatXYZDUV1*)lockVtxBuffer.Get_Vertex_Array();
ib = lockIdxBuffer.Get_Index_Array();
// Add to the index buffer & vertex buffer.
Int curRoad;
if (true) {
// Do road segments.
TCorner corner;
for (corner = SEGMENT; corner < NUM_JOINS; corner = (TCorner)(corner+1)) {
for (curRoad=0; curRoadm_roadTypes[m_curRoadType].setNumVertices(m_curNumRoadVertices);
this->m_roadTypes[m_curRoadType].setNumIndices(m_curNumRoadIndices);
}
//=============================================================================
// W3DRoadBuffer::loadRoadSegment
//=============================================================================
/** Loads a road segment into the vertex buffer for drawing. */
//=============================================================================
void W3DRoadBuffer::loadRoadSegment(UnsignedShort *ib, VertexFormatXYZDUV1 *vb, RoadSegment *pRoad)
{
if (pRoad->m_uniqueID != m_curUniqueID) {
return;
}
// Throw out segs out of view.
if (pRoad->m_pt1.loc.X + pRoad->m_scale/2 < this->m_minX &&
pRoad->m_pt2.loc.X + pRoad->m_scale/2 < this->m_minX) {
return;
}
if (pRoad->m_pt1.loc.X - pRoad->m_scale/2 > this->m_maxX &&
pRoad->m_pt2.loc.X - pRoad->m_scale/2 > this->m_maxX) {
return;
}
// Throw out segs out of view.
if (pRoad->m_pt1.loc.Y + pRoad->m_scale/2 < this->m_minY &&
pRoad->m_pt2.loc.Y + pRoad->m_scale/2 < this->m_minY) {
return;
}
if (pRoad->m_pt1.loc.Y - pRoad->m_scale/2 > this->m_maxY &&
pRoad->m_pt2.loc.Y - pRoad->m_scale/2 > this->m_maxY) {
return;
}
Int curVertex = m_curNumRoadVertices;
if (curVertex+pRoad->GetNumVertex() >= m_maxRoadVertex) return;
if (m_curNumRoadIndices+pRoad->GetNumIndex() >= m_maxRoadIndex) return;
m_curNumRoadVertices += pRoad->GetVertices(vb+curVertex, pRoad->GetNumVertex());
m_curNumRoadIndices += pRoad->GetIndices(ib+m_curNumRoadIndices, pRoad->GetNumIndex(), curVertex);
}
//=============================================================================
// W3DRoadBuffer::moveRoadSegTo
//=============================================================================
/** Moves a road segment. */
//=============================================================================
void W3DRoadBuffer::moveRoadSegTo(Int fromNdx, Int toNdx)
{
if (fromNdx<0 || fromNdx>=m_numRoads || toNdx<0 || toNdx>=m_numRoads) {
#ifdef _DEBUG
DEBUG_LOG(("bad moveRoadSegTo\n"));
#endif
return;
}
if (fromNdx == toNdx) return;
RoadSegment cur = m_roads[fromNdx];
Int i;
if (fromNdxtoNdx; i--) {
m_roads[i] = m_roads[i-1];
}
}
m_roads[toNdx] = cur;
}
//=============================================================================
// W3DRoadBuffer::checkLinkBefore
//=============================================================================
/** Checks to see if any segments need to link before this one. */
//=============================================================================
void W3DRoadBuffer::checkLinkBefore(Int ndx)
{
// Check for things whose loc == our loc2.
if (m_roads[ndx].m_pt2.count != 1) {
return;
}
Vector2 loc2 = m_roads[ndx].m_pt2.loc;
#ifdef _DEBUG
DEBUG_ASSERTLOG(m_roads[ndx].m_pt1.loc == m_roads[ndx+1].m_pt2.loc, ("Bad link\n"));
if (ndx>0) {
DEBUG_ASSERTLOG(m_roads[ndx].m_pt2.loc != m_roads[ndx-1].m_pt1.loc, ("Bad Link\n"));
}
#endif
Int endOfCurSeg = ndx+1;
while (endOfCurSeg= m_numRoads-1) {
return;
}
Vector2 loc1 = m_roads[ndx].m_pt1.loc;
#ifdef _DEBUG
DEBUG_ASSERTLOG(m_roads[ndx].m_pt2.loc == m_roads[ndx-1].m_pt1.loc, ("Bad link\n"));
#endif
Int checkNdx = ndx+1;
while (checkNdx < m_numRoads && ndx < m_numRoads-1) {
if (m_roads[checkNdx].m_pt2.loc == loc1) {
#ifdef _DEBUG
DEBUG_ASSERTLOG(m_roads[checkNdx].m_pt2.count==1, ("Bad count\n"));
#endif
ndx++;
moveRoadSegTo(checkNdx, ndx);
loc1 = m_roads[ndx].m_pt1.loc;
if (m_roads[ndx].m_pt1.count != 1) return;
} else if (m_roads[checkNdx].m_pt1.loc == loc1) {
#ifdef _DEBUG
DEBUG_ASSERTLOG(m_roads[checkNdx].m_pt1.count==1, ("Wrong m_pt1.count.\n"));
if ( m_roads[checkNdx].m_pt1.count!=1) {
::OutputDebugString("Wrong m_pt1.count.\n");
}
#endif
flipTheRoad(&m_roads[checkNdx]);
ndx++;
moveRoadSegTo(checkNdx, ndx);
loc1 = m_roads[ndx].m_pt1.loc;
if (m_roads[ndx].m_pt1.count != 1) return;
} else {
checkNdx++;
}
}
}
static Bool warnSegments = true;
#define CHECK_SEGMENTS {if (m_numRoads >= m_maxRoadSegments) { if (warnSegments) DEBUG_LOG(("****** Too many road segments. Need to increase ini values. See john a.\n")); warnSegments = false; return;}}
//=============================================================================
// W3DRoadBuffer::addMapObject
//=============================================================================
/** Loads the roads from the map objects. */
//=============================================================================
void W3DRoadBuffer::addMapObject(RoadSegment *pRoad, Bool updateTheCounts)
{
RoadSegment cur = *pRoad;
Vector2 loc1, loc2;
loc1 = cur.m_pt1.loc;
loc2 = cur.m_pt2.loc;
Vector2 roadVector(loc2.X-loc1.X, loc2.Y-loc1.Y);
Vector2 roadNormal(-roadVector.Y, roadVector.X);
roadNormal.Normalize();
roadNormal *= (cur.m_scale*cur.m_widthInTexture/2.0f);
cur.m_pt1.top = loc1+roadNormal;
cur.m_pt1.bottom = loc1 - roadNormal;
cur.m_pt2.top = loc2+roadNormal;
cur.m_pt2.bottom = loc2 - roadNormal;
if (updateTheCounts) {
updateCounts(&cur);
}
CHECK_SEGMENTS;
Int i;
Bool flip = false;
Bool addBefore = false;
Bool addAfter = false;
Bool bothMatch = false;
for (i=0; iaddIndex; i--) {
m_roads[i] = m_roads[i-1];
}
}
m_roads[addIndex] = cur;
if (flip) {
flipTheRoad(&m_roads[addIndex]);
}
m_numRoads++;
if (addBefore) {
checkLinkBefore(addIndex);
} else if (addAfter) {
checkLinkAfter(addIndex);
}
}
//=============================================================================
// W3DRoadBuffer::addMapObjects
//=============================================================================
/** Loads the roads from the map objects. */
//=============================================================================
void W3DRoadBuffer::addMapObjects()
{
MapObject *pMapObj;
MapObject *pMapObj2;
for (pMapObj = MapObject::getFirstMapObject(); pMapObj; pMapObj = pMapObj->getNext()) {
if (m_numRoads >= m_maxRoadSegments) {
break;
}
if (pMapObj->getFlag(FLAG_ROAD_POINT1)) {
pMapObj2 = pMapObj->getNext();
#ifdef _DEBUG
DEBUG_ASSERTLOG(pMapObj2 && pMapObj2->getFlag(FLAG_ROAD_POINT2), ("Bad Flag\n"));
#endif
if (pMapObj2==NULL) break;
if (!pMapObj2->getFlag(FLAG_ROAD_POINT2)) continue;
Vector2 loc1, loc2;
loc1.Set(pMapObj->getLocation()->x, pMapObj->getLocation()->y);
loc2.Set(pMapObj2->getLocation()->x, pMapObj2->getLocation()->y);
if (loc1.X==loc2.X && loc1.Y==loc2.Y) {
loc2.X += 0.25;
}
RoadSegment curRoad;
curRoad.m_scale = DEFAULT_ROAD_SCALE;
curRoad.m_widthInTexture = 1.0f;
curRoad.m_uniqueID = 1;
Bool found = false;
TerrainRoadType *road = TheTerrainRoads->findRoad( pMapObj->getName() );
if( road )
{
curRoad.m_widthInTexture = road->getRoadWidthInTexture();
curRoad.m_scale = road->getRoadWidth();
curRoad.m_uniqueID = road->getID();
found = TRUE;
} // end if
#ifdef LOAD_TEST_ASSETS
const Real DEFAULT_SCALE = 30;
if (!found) {
Int i;
for (i=0; igetName() == m_roadTypes[i].getPath()) {
curRoad.m_scale = DEFAULT_SCALE;
curRoad.m_uniqueID = m_roadTypes[i].getUniqueID();
found = true;
}
}
}
if (!found && m_curOpenRoadgetName(), m_maxUID);
m_curOpenRoad++;
}
#endif
curRoad.m_pt1.loc = loc1;
curRoad.m_pt1.isAngled = pMapObj->getFlag(FLAG_ROAD_CORNER_ANGLED);
curRoad.m_pt1.isJoin = pMapObj->getFlag(FLAG_ROAD_JOIN);
curRoad.m_pt2.loc =loc2;
curRoad.m_pt2.isAngled = pMapObj2->getFlag(FLAG_ROAD_CORNER_ANGLED);
curRoad.m_pt2.isJoin = pMapObj2->getFlag(FLAG_ROAD_JOIN);
curRoad.m_type = SEGMENT;
curRoad.m_curveRadius = pMapObj->getFlag(FLAG_ROAD_CORNER_TIGHT)?TIGHT_CORNER_RADIUS:CORNER_RADIUS;
addMapObject(&curRoad, true);
pMapObj = pMapObj2;
}
}
Int curCount = m_numRoads;
Int i;
m_numRoads = 0;
for (i=0; im_pt1.last = true;
pRoad->m_pt2.last = true;
pRoad->m_pt1.multi = false;
pRoad->m_pt2.multi = false;
pRoad->m_pt1.count = 0;
pRoad->m_pt2.count = 0;
Vector2 loc1, loc2;
loc1 = pRoad->m_pt1.loc;
loc2 = pRoad->m_pt2.loc;
Int i;
for (i=0; im_uniqueID) {
continue;
}
if ((m_roads[i].m_pt1.loc == loc1)) {
m_roads[i].m_pt1.count++;
pRoad->m_pt1.count++;
}
if ((m_roads[i].m_pt1.loc == loc2)) {
m_roads[i].m_pt1.count++;
pRoad->m_pt2.count++;
}
if ((m_roads[i].m_pt2.loc ==loc1)) {
m_roads[i].m_pt2.count++;
pRoad->m_pt1.count++;
}
if ((m_roads[i].m_pt2.loc == loc2)) {
m_roads[i].m_pt2.count++;
pRoad->m_pt2.count++;
}
m_roads[i].m_pt1.multi = m_roads[i].m_pt1.count > 1;
m_roads[i].m_pt2.multi = m_roads[i].m_pt2.count > 1;
}
pRoad->m_pt1.multi = pRoad->m_pt1.count > 1;
pRoad->m_pt2.multi = pRoad->m_pt2.count > 1;
}
//=============================================================================
// W3DRoadBuffer::updateCountsAndFlags
//=============================================================================
/** Updates the count and last fields. */
//=============================================================================
void W3DRoadBuffer::updateCountsAndFlags()
{
Int i, j;
for (i=0; i0; j--) {
Vector2 loc1, loc2;
loc1 = m_roads[j].m_pt1.loc;
loc2 = m_roads[j].m_pt2.loc;
for (i=0; iloc - loc;
v1.Normalize();
Vector2 v2 = pr2->loc - loc;
v2.Normalize();
Vector2 v3 = pr3->loc - loc;
v3.Normalize();
Real dot12 = v1.Dot_Product(v1, v2);
Real dot13 = v1.Dot_Product(v1, v3);
Real dot32 = v1.Dot_Product(v3, v2);
// The greatest negative dot product is the pair that is heading most opposite each other.
Bool do12 = false;
Bool do13 = false;
Bool do32 = false;
if (dot12 cos60) {
// The arm of the tee is slanted, so do a slant tee.
Real angle = (PI/2); // 90 degrees.
Real xpdct = Vector3::Cross_Product_Z(Vector3(upVector.X,upVector.Y,0), Vector3(decider.X, decider.Y,0));
Bool mirror = false;
if (xpdct<0) {
angle = -angle;
mirror = true;
}
upVector.Normalize();
upVector *= 0.5*scale; // we are offseting one half road width.
Vector2 teeVector(upVector);
teeVector.Rotate(angle);
Bool flip;
if (do12) {
flip = xpSign(teeVector, v3) == 1;
offsetH(pc1, pc2, pc3, loc, upVector, teeVector, flip, mirror, m_roads[index1].m_widthInTexture);
}
if (do13) {
flip = xpSign(teeVector, v2) == 1;
offsetH(pc1, pc3, pc2, loc, upVector, teeVector, flip, mirror, m_roads[index1].m_widthInTexture);
}
if (do32) {
flip = xpSign(teeVector, v1) == 1;
offsetH(pc3, pc2, pc1, loc, upVector, teeVector, flip, mirror, m_roads[index1].m_widthInTexture);
}
pc1->last = true;
pc1->count = 0;
pc2->last = true;
pc2->count = 0;
pc3->last = true;
pc3->count = 0;
CHECK_SEGMENTS;
m_roads[m_numRoads].m_pt1.loc.Set(loc);
m_roads[m_numRoads].m_pt2.loc.Set(loc+teeVector);
m_roads[m_numRoads].m_pt1.last = true; // if not, that one will clear flag in prior loop.
m_roads[m_numRoads].m_pt2.last = true; // if not, that one will clear flag in prior loop.
m_roads[m_numRoads].m_scale = m_roads[index1].m_scale;
m_roads[m_numRoads].m_widthInTexture = m_roads[index1].m_widthInTexture;
m_roads[m_numRoads].m_pt1.count = -3;
m_roads[m_numRoads].m_type = flip?THREE_WAY_H_FLIP:THREE_WAY_H;
m_roads[m_numRoads].m_uniqueID = m_roads[index1].m_uniqueID;
m_numRoads++;
} else {
// Do a not slanted tee.
Real angle = (PI/2); // 90 degrees.
Real xpdct = Vector3::Cross_Product_Z(Vector3(upVector.X,upVector.Y,0), Vector3(decider.X, decider.Y,0));
if (xpdct<0) angle = -angle;
upVector.Normalize();
upVector *= 0.5*scale; // we are offseting one half road width.
Vector2 teeVector(upVector);
teeVector.Rotate(angle);
if (do12) {
offset3Way(pc1, pc2, pc3, loc, upVector, teeVector, m_roads[index1].m_widthInTexture);
}
if (do13) {
offset3Way(pc1, pc3, pc2, loc, upVector, teeVector, m_roads[index1].m_widthInTexture);
}
if (do32) {
offset3Way(pc3, pc2, pc1, loc, upVector, teeVector, m_roads[index1].m_widthInTexture);
}
pc1->last = true;
pc1->count = 0;
pc2->last = true;
pc2->count = 0;
pc3->last = true;
pc3->count = 0;
CHECK_SEGMENTS;
m_roads[m_numRoads].m_pt1.loc.Set(loc);
m_roads[m_numRoads].m_pt2.loc.Set(loc+teeVector);
m_roads[m_numRoads].m_pt1.last = true; // if not, that one will clear flag in prior loop.
m_roads[m_numRoads].m_pt2.last = true; // if not, that one will clear flag in prior loop.
m_roads[m_numRoads].m_scale = m_roads[index1].m_scale;
m_roads[m_numRoads].m_widthInTexture = m_roads[index1].m_widthInTexture;
m_roads[m_numRoads].m_pt1.count = -3;
m_roads[m_numRoads].m_type = TEE;
m_roads[m_numRoads].m_uniqueID = m_roads[index1].m_uniqueID;
m_numRoads++;
}
}
//=============================================================================
// W3DRoadBuffer::insertY
//=============================================================================
/** Inserts a Y intersection if the corner meets "Y" criteria. */
//=============================================================================
Bool W3DRoadBuffer::insertY(Vector2 loc, Int index1, Real scale)
{
// pr1-3 point to the points on the segments that form the tee.
// They are the points on the segments that are != loc.
TRoadPt *pr1=NULL;
TRoadPt *pr2=NULL;
TRoadPt *pr3=NULL;
// pc1-3 point to the center points of the segments. These are the
// points that are at loc.
TRoadPt *pc1=NULL;
TRoadPt *pc2=NULL;
TRoadPt *pc3=NULL;
if (m_roads[index1].m_pt1.loc == loc) {
pr1 = &m_roads[index1].m_pt2;
pc1 = &m_roads[index1].m_pt1;
} else {
pr1 = &m_roads[index1].m_pt1;
pc1 = &m_roads[index1].m_pt2;
}
Int i;
Int index2=0;
Int index3=0;
for (i = index1+1; iloc - loc;
v1.Normalize();
Vector2 v2 = pr2->loc - loc;
v2.Normalize();
Vector2 v3 = pr3->loc - loc;
v3.Normalize();
Bool do12 = false;
Bool do13 = false;
Bool do32 = false;
Real dot12 = v1.Dot_Product(v1, v2);
Real dot13 = v1.Dot_Product(v1, v3);
Real dot32 = v1.Dot_Product(v3, v2);
Real score12 = 2.0f;
Real score13 = 2.0f;
Real score32 = 2.0f;
// const Real cos60 = 0.5f;
const Real cos30 = 0.866f;
const Real cos45 = 0.707f;
if (dot12 < (-cos30)) return false; // Too close to a straigh line, do a straight side tee.
if (dot13 < (-cos30)) return false; // Too close to a straigh line, do a straight side tee.
if (dot32 < (-cos30)) return false; // Too close to a straigh line, to a straight side tee.
Int s1 = 0;
Int s2 = xpSign(v1, v2);
Int s3 = xpSign(v1, v3);
if (s2!=s3 && (s2+s3==0)) {
Vector2 v1_90(-v1.Y, v1.X);
if (xpSign(v1_90, v2) == 1 && xpSign(v1_90, v3) == 1) {
// s2 & s3 could be Y legs.
do32 = true;
score32 = fabs(dot12+cos45) + fabs(dot13+cos45);
}
}
s1 = xpSign(v3, v1);
s2 = xpSign(v3, v2);
if (s2!=s1 && (s2+s1==0)) {
// s2 & s3 could be Y legs.
Vector2 v3_90(-v3.Y, v3.X);
if (xpSign(v3_90, v2) == 1 && xpSign(v3_90, v1) == 1) {
do12 = true;
score12 = fabs(dot13+cos45) + fabs(dot32+cos45);
}
}
s1 = xpSign(v2, v1);
s3 = xpSign(v2, v3);
if (s3!=s1 && (s3+s1==0)) {
// s2 & s3 could be Y legs.
Vector2 v2_90(-v2.Y, v2.X);
if (xpSign(v2_90, v3) == 1 && xpSign(v2_90, v1) == 1) {
do13 = true;
score13 = fabs(dot12+cos45) + fabs(dot32+cos45);
}
}
if (score12last = true;
pc1->count = 0;
pc2->last = true;
pc2->count = 0;
pc3->last = true;
pc3->count = 0;
if (m_numRoads >= m_maxRoadSegments) return false;
m_roads[m_numRoads].m_pt1.loc.Set(loc);
m_roads[m_numRoads].m_pt2.loc.Set(loc+teeVector);
m_roads[m_numRoads].m_pt1.last = true; // if not, that one will clear flag in prior loop.
m_roads[m_numRoads].m_pt2.last = true; // if not, that one will clear flag in prior loop.
m_roads[m_numRoads].m_scale = m_roads[index1].m_scale;
m_roads[m_numRoads].m_widthInTexture = m_roads[index1].m_widthInTexture;
m_roads[m_numRoads].m_pt1.count = -3;
m_roads[m_numRoads].m_type = THREE_WAY_Y;
m_roads[m_numRoads].m_uniqueID = m_roads[index1].m_uniqueID;
m_numRoads++;
return true;
}
//=============================================================================
// W3DRoadBuffer::offset3Way
//=============================================================================
/** Offsets the points coming into a 3 way intersection so that they move to
the join points of the 3 way intersection. */
//=============================================================================
void W3DRoadBuffer::offset3Way(TRoadPt *pc1, TRoadPt *pc2, TRoadPt *pc3, Vector2 loc, Vector2 upVector,
Vector2 teeVector, Real widthInTexture)
{
pc1->loc = loc - upVector;
pc2->loc = loc + upVector;
pc3->loc = loc + teeVector;
// Adjust the top & bottoms, so they merge smoothly into the tee.
// Make sure the t vector goes right with respect to the up vector.
Real xpdct = Vector3::Cross_Product_Z(Vector3(upVector.X,upVector.Y,0), Vector3(teeVector.X, teeVector.Y,0));
Vector2 rightTee = teeVector;
if (xpdct<0) {
rightTee.X = -teeVector.X;
rightTee.Y = -teeVector.Y;
}
rightTee *= (widthInTexture);
xpdct = Vector3::Cross_Product_Z(Vector3(upVector.X, upVector.Y, 0), Vector3(pc1->top.X-pc1->loc.X, pc1->top.Y-pc1->loc.Y,0));
if (xpdct>0) {
pc1->bottom = pc1->loc - rightTee;
pc1->top = pc1->loc + rightTee;
} else {
pc1->bottom = pc1->loc + rightTee;
pc1->top = pc1->loc - rightTee;
}
xpdct = Vector3::Cross_Product_Z(Vector3(upVector.X, upVector.Y, 0), Vector3(pc2->top.X-pc2->loc.X, pc2->top.Y-pc2->loc.Y,0));
if (xpdct>0) {
pc2->bottom = pc2->loc - rightTee;
pc2->top = pc2->loc + rightTee;
} else {
pc2->bottom = pc2->loc + rightTee;
pc2->top = pc2->loc - rightTee;
}
upVector *= (widthInTexture);
xpdct = Vector3::Cross_Product_Z(Vector3(rightTee.X, rightTee.Y, 0), Vector3(pc3->top.X-pc3->loc.X, pc3->top.Y-pc3->loc.Y,0));
if (xpdct<0) {
pc3->bottom = pc3->loc - upVector;
pc3->top = pc3->loc + upVector;
} else {
pc3->bottom = pc3->loc + upVector;
pc3->top = pc3->loc - upVector;
}
}
//=============================================================================
// W3DRoadBuffer::offsetH
//=============================================================================
/** Offsets the points coming into a 3 way intersection so that they move to
the join points of the 3 way intersection. */
//=============================================================================
void W3DRoadBuffer::offsetH(TRoadPt *pc1, TRoadPt *pc2, TRoadPt *pc3, Vector2 loc,
Vector2 upVector, Vector2 teeVector,
Bool flip, Bool mirror, Real widthInTexture)
{
if (flip != mirror) {
pc1->loc = loc - upVector*2.05f;
pc2->loc = loc + upVector*.46f;
} else {
pc1->loc = loc - upVector*.46f;
pc2->loc = loc + upVector*2.05f;
}
//pc3->loc = loc + teeVector;
// Adjust the top & bottoms, so they merge smoothly into the tee.
// Make sure the t vector goes right with respect to the up vector.
Real xpdct = Vector3::Cross_Product_Z(Vector3(upVector.X,upVector.Y,0), Vector3(teeVector.X, teeVector.Y,0));
Vector2 rightTee = teeVector;
if (xpdct<0) {
rightTee.X = -teeVector.X;
rightTee.Y = -teeVector.Y;
}
rightTee *= (widthInTexture);
xpdct = Vector3::Cross_Product_Z(Vector3(upVector.X, upVector.Y, 0), Vector3(pc1->top.X-pc1->loc.X, pc1->top.Y-pc1->loc.Y,0));
if (xpdct>0) {
pc1->bottom = pc1->loc - rightTee;
pc1->top = pc1->loc + rightTee;
} else {
pc1->bottom = pc1->loc + rightTee;
pc1->top = pc1->loc - rightTee;
}
xpdct = Vector3::Cross_Product_Z(Vector3(upVector.X, upVector.Y, 0), Vector3(pc2->top.X-pc2->loc.X, pc2->top.Y-pc2->loc.Y,0));
if (xpdct>0) {
pc2->bottom = pc2->loc - rightTee;
pc2->top = pc2->loc + rightTee;
} else {
pc2->bottom = pc2->loc + rightTee;
pc2->top = pc2->loc - rightTee;
}
Vector2 arm = teeVector;
if (flip) {
arm.Rotate(PI/4);
} else {
arm.Rotate(-PI/4);
}
Vector2 armNormal(-arm.Y, arm.X);
armNormal *= widthInTexture;
pc3->loc += arm*2.10f;
xpdct = xpSign(arm, pc3->top-loc);
if (xpdct==1) {
pc3->top = pc3->loc + armNormal;
pc3->bottom = pc3->loc - armNormal;
} else {
pc3->top = pc3->loc - armNormal;
pc3->bottom = pc3->loc + armNormal;
}
}
//=============================================================================
// W3DRoadBuffer::offsetY
//=============================================================================
/** Offsets the points coming into a 3 way intersection so that they move to
the join points of the 3 way intersection. */
//=============================================================================
void W3DRoadBuffer::offsetY(TRoadPt *pc1, TRoadPt *pc2, TRoadPt *pc3, Vector2 loc, Vector2 upVector,
Real widthInTexture)
{
pc3->loc += upVector*0.55f;
pc3->top += upVector*0.55f;
pc3->bottom += upVector*0.55f;
// Adjust the bottom, so they merge smoothly into the tee.
// Make sure the t vector goes right with respect to the up vector.
Vector2 arm = upVector;
arm.Rotate(3*PI/4);
Vector2 armNormal(-arm.Y, arm.X);
armNormal *= widthInTexture;
pc2->loc += arm*1.1f;
Int xpdct = xpSign(arm, pc2->top-loc);
if (xpdct==1) {
pc2->top = pc2->loc + armNormal;
pc2->bottom = pc2->loc - armNormal;
} else {
pc2->top = pc2->loc - armNormal;
pc2->bottom = pc2->loc + armNormal;
}
arm = upVector;
arm.Rotate(-3*PI/4);
armNormal.Set(-arm.Y, arm.X);
armNormal *= widthInTexture;
pc1->loc += arm*1.1f;
xpdct = xpSign(arm, pc1->top-loc);
if (xpdct==1) {
pc1->top = pc1->loc + armNormal;
pc1->bottom = pc1->loc - armNormal;
} else {
pc1->top = pc1->loc - armNormal;
pc1->bottom = pc1->loc + armNormal;
}
}
//=============================================================================
// W3DRoadBuffer::offset4Way
//=============================================================================
/** Offsets the points coming into a 4 way intersection so that they move to
the join points of the 4 way intersection. */
//=============================================================================
void W3DRoadBuffer::offset4Way(TRoadPt *pc1, TRoadPt *pc2, TRoadPt *pc3, TRoadPt *pr3,
TRoadPt *pc4, Vector2 loc, Vector2 alignVector, Real widthInTexture)
{
pc1->loc = loc - alignVector;
pc2->loc = loc + alignVector;
Vector2 v3 = pr3->loc - loc;
Real angle = (PI/2); // 90 degrees.
Real xpdct = Vector3::Cross_Product_Z(Vector3(alignVector.X,alignVector.Y,0), Vector3(v3.X, v3.Y,0));
if (xpdct<0) angle = -angle;
Vector2 teeVector(alignVector);
teeVector.Rotate(angle);
pc3->loc = loc + teeVector;
pc4->loc = loc - teeVector;
Vector2 realTee(alignVector);
realTee.Rotate(PI/2);
realTee *= widthInTexture;
// Fix up the top & bottom points.
Vector3 align3(alignVector.X,alignVector.Y,0);
xpdct = Vector3::Cross_Product_Z(align3, Vector3(pc1->top.X-pc1->loc.X, pc1->top.Y-pc1->loc.Y,0));
if (xpdct>0) {
pc1->bottom = pc1->loc - realTee;
pc1->top = pc1->loc + realTee;
} else {
pc1->bottom = pc1->loc + realTee;
pc1->top = pc1->loc - realTee;
}
xpdct = Vector3::Cross_Product_Z(align3, Vector3(pc2->top.X-pc2->loc.X, pc2->top.Y-pc2->loc.Y,0));
if (xpdct>0) {
pc2->bottom = pc2->loc - realTee;
pc2->top = pc2->loc + realTee;
} else {
pc2->bottom = pc2->loc + realTee;
pc2->top = pc2->loc - realTee;
}
alignVector *= widthInTexture;
Vector3 tee3(realTee.X,realTee.Y,0);
xpdct = Vector3::Cross_Product_Z(tee3, Vector3(pc3->top.X-pc3->loc.X, pc3->top.Y-pc3->loc.Y,0));
if (xpdct<0) {
pc3->bottom = pc3->loc - alignVector;
pc3->top = pc3->loc + alignVector;
} else {
pc3->bottom = pc3->loc + alignVector;
pc3->top = pc3->loc - alignVector;
}
xpdct = Vector3::Cross_Product_Z(tee3, Vector3(pc4->top.X-pc4->loc.X, pc4->top.Y-pc4->loc.Y,0));
if (xpdct<0) {
pc4->bottom = pc4->loc - alignVector;
pc4->top = pc4->loc + alignVector;
} else {
pc4->bottom = pc4->loc + alignVector;
pc4->top = pc4->loc - alignVector;
}
pc1->last = true;
pc1->count = 0;
pc2->last = true;
pc2->count = 0;
pc3->last = true;
pc3->count = 0;
pc4->last = true;
pc4->count = 0;
}
//=============================================================================
// W3DRoadBuffer::insert4Way
//=============================================================================
/** Inserts a 4 way intersection. */
//=============================================================================
void W3DRoadBuffer::insert4Way(Vector2 loc, Int index1, Real scale)
{
// pr1-4 point to the points on the segments that form the tee.
// They are the points on the segments that are != loc.
TRoadPt *pr1=NULL;
TRoadPt *pr2=NULL;
TRoadPt *pr3=NULL;
TRoadPt *pr4=NULL;
// pc1-4 point to the center points of the segments. These are the
// points that are at loc.
TRoadPt *pc1=NULL;
TRoadPt *pc2=NULL;
TRoadPt *pc3=NULL;
TRoadPt *pc4=NULL;
if (m_roads[index1].m_pt1.loc == loc) {
pr1 = &m_roads[index1].m_pt2;
pc1 = &m_roads[index1].m_pt1;
} else {
pr1 = &m_roads[index1].m_pt1;
pc1 = &m_roads[index1].m_pt2;
}
Int i;
for (i = index1+1; iloc - loc;
v1.Normalize();
Vector2 v2 = pr2->loc - loc;
v2.Normalize();
Vector2 v3 = pr3->loc - loc;
v3.Normalize();
Vector2 v4 = pr4->loc - loc;
v4.Normalize();
Real dot12 = v1.Dot_Product(v1, v2);
Real dot13 = v1.Dot_Product(v1, v3);
Real dot14 = v1.Dot_Product(v1, v4);
Real dot23 = v1.Dot_Product(v2, v3);
Real dot24 = v1.Dot_Product(v2, v4);
Real dot34 = v1.Dot_Product(v3, v4);
// The most negative dot product is the pair that is heading most opposite each other.
Int curPair = 12;
Real curDot = dot12;
if (dot13=0) {
if (m_roads[i].m_pt1.loc == m_roads[segmentStartIndex].m_pt2.loc) {
if (m_roads[segmentStartIndex].m_pt2.count==1 && m_roads[i].m_pt1.count==1) {
insertCurveSegmentAt(i, segmentStartIndex);
}
}
segmentStartIndex = -1;
}
}
}
//=============================================================================
// W3DRoadBuffer::findCrossTypeJoinVector
//=============================================================================
/** Finds a road segment of different type && sets the join vector. */
//=============================================================================
Int W3DRoadBuffer::findCrossTypeJoinVector(Vector2 loc, Vector2 *joinVector, Int uniqueID)
{
Vector2 newVector = *joinVector;
// Insert the curve segments.
Int numRoadSegments = m_numRoads;
Int i;
for (i=0; i loc2.X) bounds.lo.x = loc2.X;
if (bounds.lo.y > loc2.Y) bounds.lo.y = loc2.Y;
if (bounds.hi.x < loc2.X) bounds.hi.x = loc2.X;
if (bounds.hi.y < loc2.Y) bounds.hi.y = loc2.Y;
bounds.lo.x -= m_roads[i].m_scale/2;
bounds.lo.y -= m_roads[i].m_scale/2;
bounds.hi.x += m_roads[i].m_scale/2;
bounds.hi.y += m_roads[i].m_scale/2;
if (loc.X >= bounds.lo.x && loc.Y >= bounds.lo.y && loc.X <= bounds.hi.x && loc.Y <= bounds.hi.y) {
Vector3 v1 = Vector3(loc1.X, loc1.Y, 0);
Vector3 v2 = Vector3(loc2.X, loc2.Y, 0);
LineSegClass roadLine(v1,v2);
Vector3 vLoc(loc.X, loc.Y, 0);
Vector3 ptOnLine = roadLine.Find_Point_Closest_To(vLoc);
Real dist = Vector3::Distance(ptOnLine, vLoc);
if (dist < m_roads[i].m_scale*0.55f) {
Vector2 roadVec = loc2-loc1;
if (xpSign(roadVec, *joinVector) == 1) {
roadVec.Rotate(PI/2);
} else {
roadVec.Rotate(-PI/2);
}
newVector = roadVec;
*joinVector = newVector;
return m_roads[i].m_uniqueID;
}
}
}
return 0;
}
//=============================================================================
// W3DRoadBuffer::adjustStacking
//=============================================================================
/** Adjusts the stacking order. */
//=============================================================================
void W3DRoadBuffer::adjustStacking(Int topUniqueID, Int bottomUniqueID)
{
Int i, j;
for (i=0; i=m_maxRoadTypes) return;
for (j=0; j=m_maxRoadTypes) return;
if (m_roadTypes[i].getStacking() > m_roadTypes[j].getStacking()) {
return; // It's already on top.
}
Int newStacking = m_roadTypes[j].getStacking();
for (j=0; jnewStacking) {
m_roadTypes[j].setStacking(m_roadTypes[j].getStacking()+1);
}
}
m_roadTypes[i].setStacking(newStacking+1);
}
//=============================================================================
// W3DRoadBuffer::insertCrossTypeJoins
//=============================================================================
/** Inserts alpha blend type joins at open ends. */
//=============================================================================
void W3DRoadBuffer::insertCrossTypeJoins(void)
{
// Insert the curve segments.
Int numRoadSegments = m_numRoads;
Int i;
for (i=0; i 0) {
pr1 = &m_roads[ndx1].m_pt1.loc;
pr2 = &m_roads[ndx1].m_pt2.loc;
pr3 = &m_roads[ndx2].m_pt1.loc;
pr4 = &m_roads[ndx2].m_pt2.loc;
turnRight = true;
} else {
pr4 = &m_roads[ndx1].m_pt1.loc;
pr3 = &m_roads[ndx1].m_pt2.loc;
pr2 = &m_roads[ndx2].m_pt1.loc;
pr1 = &m_roads[ndx2].m_pt2.loc;
turnRight = false;
line1.Set(Vector3(pr1->X, pr1->Y, 0), Vector3(pr2->X, pr2->Y, 0));
line2.Set(Vector3(pr3->X, pr3->Y, 0), Vector3(pr4->X, pr4->Y, 0));
}
Real angle = WWMath::Acos(curSin);
Real count = angle / (PI/6.0f); // number of 30 degree steps.
if (count<0.9 || m_roads[ndx1].m_pt1.isAngled) {
miter(ndx1, ndx2);
return;
}
Vector3 offset1(radius*line1.Get_Dir());
Vector3 offset2(radius*line2.Get_Dir());
offset1.Rotate_Z(-PI/2);
offset2.Rotate_Z(-PI/2);
Vector3 p1 = Vector3(pr1->X, pr1->Y, 0)+offset1;
Vector3 p2 = Vector3(pr2->X, pr2->Y, 0)+offset1;
LineSegClass offsetLine1(p1,p2);
p1 = Vector3(pr3->X, pr3->Y, 0)+offset2;
p2 = Vector3(pr4->X, pr4->Y, 0)+offset2;
LineSegClass offsetLine2(p1,p2);
Vector3 pInt1, pInt2;
Vector3 pInt3, pInt4;
Real nu; // not used.
if (offsetLine1.Find_Intersection(offsetLine2, &pInt1, &nu, &pInt2, &nu) ) {
m_roads[ndx2].m_pt2.last = true;
LineSegClass cross1(pInt1, pInt1-offset2);
LineSegClass cross2(pInt1, pInt1-offset1);
cross1.Find_Intersection(line2, &pInt1, &nu, &pInt2, &nu);
cross2.Find_Intersection(line1, &pInt3, &nu, &pInt4, &nu);
// Make sure the lines didn't clip out of existence.
Real theDot = Vector3::Dot_Product(line2.Get_Dir(), pInt1-Vector3(pr3->X, pr3->Y, 0));
if (theDot < DOT_LIMIT) {
*pr1 = originalPt;
*pr4 = originalPt;
miter(ndx1, ndx2);
return;
}
theDot = Vector3::Dot_Product(line1.Get_Dir(), Vector3(pr2->X, pr2->Y, 0)-pInt3);
if (theDot < DOT_LIMIT) {
*pr1 = originalPt;
*pr4 = originalPt;
miter(ndx1, ndx2);
return;
}
*pr4 = Vector2(pInt1.X, pInt1.Y);
Real angle = -PI/6.0f; // -30 degrees.
Vector2 pt2 = *pr4;
Vector2 pt1 = *pr3;
Vector2 direction(pt1.X-pt2.X, pt1.Y-pt2.Y);
// offset = normal of the vector from pt1 to pt2.
Vector2 centerOfCurve(-direction.Y, direction.X);
centerOfCurve.Normalize();
centerOfCurve *= m_roads[ndx1].m_curveRadius*m_roads[ndx1].m_scale;
centerOfCurve += pt2;
rotateAbout(&pt2, centerOfCurve, angle);
direction.Rotate(angle);
pt1 = pt2+direction;
m_roads[m_numRoads].m_pt1.loc=pt2;
m_roads[m_numRoads].m_pt2.loc=pt1;
CHECK_SEGMENTS;
m_roads[m_numRoads].m_pt1.last = true; // if not, that one will clear flag in prior loop.
m_roads[m_numRoads].m_pt2.last = true; // if not, that one will clear flag in prior loop.
m_roads[m_numRoads].m_scale = m_roads[ndx1].m_scale;
m_roads[m_numRoads].m_widthInTexture = m_roads[ndx1].m_widthInTexture;
m_roads[m_numRoads].m_type = CURVE;
m_roads[m_numRoads].m_curveRadius = m_roads[ndx1].m_curveRadius;
m_roads[m_numRoads].m_uniqueID = m_roads[ndx1].m_uniqueID;
m_numRoads++;
if (count > 2.0) {
Int i;
for (i=2; i 1.0) {
pt2 = *pr1;
pt1 = *pr1+*pr1-*pr2;
direction.Set(pt1.X-pt2.X, pt1.Y-pt2.Y);
pt1 = pt2+direction;
CHECK_SEGMENTS;
m_roads[m_numRoads].m_pt1.loc.Set(pt2);
m_roads[m_numRoads].m_pt2.loc.Set(pt1);
m_roads[m_numRoads].m_pt1.last = true; // if not, that one will clear flag in prior loop.
m_roads[m_numRoads].m_pt2.last = true; // if not, that one will clear flag in prior loop.
m_roads[m_numRoads].m_scale = m_roads[ndx1].m_scale;
m_roads[m_numRoads].m_widthInTexture = m_roads[ndx1].m_widthInTexture;
m_roads[m_numRoads].m_type = CURVE;
m_roads[m_numRoads].m_curveRadius = m_roads[ndx1].m_curveRadius;
m_roads[m_numRoads].m_uniqueID = m_roads[ndx1].m_uniqueID;
m_numRoads++;
}
// Recalculate top & bottom.
Vector2 roadVector = m_roads[ndx1].m_pt2.loc - m_roads[ndx1].m_pt1.loc;
Vector2 roadNormal(-roadVector.Y, roadVector.X);
roadNormal.Normalize();
roadNormal *= (m_roads[ndx1].m_scale*m_roads[ndx1].m_widthInTexture/2.0f);
m_roads[ndx1].m_pt1.top = m_roads[ndx1].m_pt1.loc+roadNormal;
m_roads[ndx1].m_pt1.bottom = m_roads[ndx1].m_pt1.loc - roadNormal;
roadVector = m_roads[ndx2].m_pt2.loc - m_roads[ndx2].m_pt1.loc;
roadNormal = Vector2(-roadVector.Y, roadVector.X);
roadNormal.Normalize();
roadNormal *= (m_roads[ndx2].m_scale*m_roads[ndx2].m_widthInTexture/2.0f);
m_roads[ndx2].m_pt2.top = m_roads[ndx2].m_pt2.loc+roadNormal;
m_roads[ndx2].m_pt2.bottom = m_roads[ndx2].m_pt2.loc - roadNormal;
}
}
//=============================================================================
// W3DRoadBuffer::rotateAbout
//=============================================================================
/** Rotates ptP about center. */
//=============================================================================
void W3DRoadBuffer::rotateAbout(Vector2 *ptP, Vector2 center, Real angle)
{
Vector2 offset;
offset.X = ptP->X - center.X;
offset.Y = ptP->Y - center.Y;
Vector2 orgOffset = offset;
offset.Rotate(angle);
offset.Y -= orgOffset.Y;
offset.X -= orgOffset.X;
*ptP += offset;
}
//-----------------------------------------------------------------------------
// Public Functions
//-----------------------------------------------------------------------------
//=============================================================================
// W3DRoadBuffer::~W3DRoadBuffer
//=============================================================================
/** Destructor. Releases w3d assets. */
//=============================================================================
W3DRoadBuffer::~W3DRoadBuffer(void)
{
freeRoadBuffers();
REF_PTR_RELEASE(m_map);
}
//=============================================================================
// W3DRoadBuffer::W3DRoadBuffer
//=============================================================================
/** Constructor. */
//=============================================================================
W3DRoadBuffer::W3DRoadBuffer(void) :
m_roads(NULL),
m_numRoads(0),
m_initialized(false),
m_map(NULL),
#ifdef LOAD_TEST_ASSETS
m_maxUID(0),
#endif // LOAD_TEST_ASSETS
m_lightsIterator(NULL),
m_maxRoadSegments(500),
m_maxRoadTypes(8),
m_maxRoadVertex(1000),
m_maxRoadIndex(2000),
m_curRoadType(0)
{
allocateRoadBuffers();
}
//=============================================================================
// W3DRoadBuffer::freeRoadBuffers
//=============================================================================
/** Frees the index and vertex buffers. */
//=============================================================================
void W3DRoadBuffer::freeRoadBuffers(void)
{
if (m_roads) {
delete[] m_roads;
m_roads = NULL;
}
if (m_roadTypes) {
delete[] m_roadTypes;
m_roadTypes = NULL;
}
}
//=============================================================================
// W3DRoadBuffer::allocateRoadBuffers
//=============================================================================
/** Allocates the index and vertex buffers. */
//=============================================================================
void W3DRoadBuffer::allocateRoadBuffers(void)
{
Int i = 0;
// save data for max limits
m_maxRoadSegments = TheGlobalData->m_maxRoadSegments;
m_maxRoadVertex = TheGlobalData->m_maxRoadVertex;
m_maxRoadIndex = TheGlobalData->m_maxRoadIndex;
m_maxRoadTypes = TheGlobalData->m_maxRoadTypes;
#ifdef LOAD_TEST_ASSETS
m_maxRoadTypes+=4;
#endif
m_curNumRoadVertices=0;
m_curNumRoadIndices=0;
m_roads = MSGNEW("RoadBuffer") RoadSegment[m_maxRoadSegments];
m_roadTypes = MSGNEW("RoadBuffer") RoadType[m_maxRoadTypes];
// load roads from INI
TerrainRoadType *road;
i = 0;
for( road = TheTerrainRoads->firstRoad(); road; road = TheTerrainRoads->nextRoad( road ) )
{
// get a path to the texture file
if( i < m_maxRoadTypes )
{
Int id = road->getID();
m_roadTypes[ i++ ].loadTexture( road->getTexture(), id );
#ifdef LOAD_TEST_ASSETS
if( m_maxUID < id )
m_maxUID = id;
#endif // LOAD_TEST_ASSETS
} // end if
} // end for road
#ifdef LOAD_TEST_ASSETS
while (i maxStacking) {
maxStacking = m_roadTypes[i].getStacking();
}
}
Int stacking;
W3DShaderManager::ShaderTypes st=W3DShaderManager::ST_ROAD_BASE; //set default shader
if (cloudTexture)
{ st=W3DShaderManager::ST_ROAD_BASE_NOISE1;
if (noiseTexture)
st=W3DShaderManager::ST_ROAD_BASE_NOISE12;
}
else
if (noiseTexture)
st=W3DShaderManager::ST_ROAD_BASE_NOISE2;
Int devicePasses = 1; //assume regular rendering
//Find number of passes required to render current shader
devicePasses=W3DShaderManager::getShaderPasses(st);
W3DShaderManager::setTexture(1,cloudTexture); //cloud
W3DShaderManager::setTexture(2,noiseTexture); //noise/lightmap
for (stacking=0; stacking <= maxStacking; stacking++) {
for (i=0; i= m_maxRoadTypes) continue;
loadLitRoadsInVertexAndIndexBuffers(pDynamicLightsIterator);
if (this->m_curNumRoadIndices == 0) continue;
if (wireframe) {
DX8Wrapper::Set_Texture(0,NULL);
} else {
m_roadTypes[i].applyTexture();
if (cloudTexture) {
DX8Wrapper::Set_Texture(1,cloudTexture);
}
}
DX8Wrapper::Set_Shader(detailAlphaShader);
//Draw all the roads.
DX8Wrapper::Draw_Triangles( 0, m_curNumRoadIndices/3, 0, m_curNumRoadVertices);
}
}
#endif
m_curRoadType = 0;
}