Correctly redo file moves/renames from rev. 8481.

--HG--
branch : trunk
rename : src/server/game/CollisionDetection/BoundingIntervalHierarchy.cpp => src/server/collision/BoundingIntervalHierarchy.cpp
rename : src/server/game/CollisionDetection/BoundingIntervalHierarchy.h => src/server/collision/BoundingIntervalHierarchy.h
rename : src/server/game/CollisionDetection/CMakeLists.txt => src/server/collision/CMakeLists.txt
rename : src/server/game/CollisionDetection/IVMapManager.h => src/server/collision/Management/IVMapManager.h
rename : src/server/game/CollisionDetection/VMapFactory.cpp => src/server/collision/Management/VMapFactory.cpp
rename : src/server/game/CollisionDetection/VMapFactory.h => src/server/collision/Management/VMapFactory.h
rename : src/server/game/CollisionDetection/VMapManager2.cpp => src/server/collision/Management/VMapManager2.cpp
rename : src/server/game/CollisionDetection/VMapManager2.h => src/server/collision/Management/VMapManager2.h
rename : src/server/game/CollisionDetection/MapTree.cpp => src/server/collision/Maps/MapTree.cpp
rename : src/server/game/CollisionDetection/MapTree.h => src/server/collision/Maps/MapTree.h
rename : src/server/game/CollisionDetection/TileAssembler.cpp => src/server/collision/Maps/TileAssembler.cpp
rename : src/server/game/CollisionDetection/TileAssembler.h => src/server/collision/Maps/TileAssembler.h
rename : src/server/game/CollisionDetection/ModelInstance.cpp => src/server/collision/Models/ModelInstance.cpp
rename : src/server/game/CollisionDetection/ModelInstance.h => src/server/collision/Models/ModelInstance.h
rename : src/server/game/CollisionDetection/WorldModel.cpp => src/server/collision/Models/WorldModel.cpp
rename : src/server/game/CollisionDetection/WorldModel.h => src/server/collision/Models/WorldModel.h
rename : src/server/game/CollisionDetection/VMapDefinitions.h => src/server/collision/VMapDefinitions.h
rename : src/server/game/CollisionDetection/VMapTools.h => src/server/collision/VMapTools.h
This commit is contained in:
Xanadu
2010-07-17 03:50:43 +02:00
parent d8e1ba0c82
commit 7dd2dc9181
18 changed files with 0 additions and 0 deletions
@@ -1,304 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* 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.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "BIH.h"
void BIH::buildHierarchy(std::vector<uint32> &tempTree, buildData &dat, BuildStats &stats)
{
// create space for the first node
tempTree.push_back(3 << 30); // dummy leaf
tempTree.insert(tempTree.end(), 2, 0);
//tempTree.add(0);
// seed bbox
AABound gridBox = { bounds.low(), bounds.high() };
AABound nodeBox = gridBox;
// seed subdivide function
subdivide(0, dat.numPrims - 1, tempTree, dat, gridBox, nodeBox, 0, 1, stats);
}
void BIH::subdivide(int left, int right, std::vector<uint32> &tempTree, buildData &dat, AABound &gridBox, AABound &nodeBox, int nodeIndex, int depth, BuildStats &stats)
{
if ((right - left + 1) <= dat.maxPrims || depth >= MAX_STACK_SIZE)
{
// write leaf node
stats.updateLeaf(depth, right - left + 1);
createNode(tempTree, nodeIndex, left, right);
return;
}
// calculate extents
int axis = -1, prevAxis, rightOrig;
float clipL = G3D::fnan(), clipR = G3D::fnan(), prevClip = G3D::fnan();
float split = G3D::fnan(), prevSplit;
bool wasLeft = true;
while (true)
{
prevAxis = axis;
prevSplit = split;
// perform quick consistency checks
Vector3 d( gridBox.hi - gridBox.lo );
if (d.x < 0 || d.y < 0 || d.z < 0)
throw std::logic_error("negative node extents");
for (int i = 0; i < 3; i++)
{
if (nodeBox.hi[i] < gridBox.lo[i] || nodeBox.lo[i] > gridBox.hi[i])
{
//UI.printError(Module.ACCEL, "Reached tree area in error - discarding node with: %d objects", right - left + 1);
throw std::logic_error("invalid node overlap");
}
}
// find longest axis
axis = d.primaryAxis();
split = 0.5f * (gridBox.lo[axis] + gridBox.hi[axis]);
// partition L/R subsets
clipL = -G3D::inf();
clipR = G3D::inf();
rightOrig = right; // save this for later
float nodeL = G3D::inf();
float nodeR = -G3D::inf();
for (int i = left; i <= right;)
{
int obj = dat.indices[i];
float minb = dat.primBound[obj].low()[axis];
float maxb = dat.primBound[obj].high()[axis];
float center = (minb + maxb) * 0.5f;
if (center <= split)
{
// stay left
i++;
if (clipL < maxb)
clipL = maxb;
}
else
{
// move to the right most
int t = dat.indices[i];
dat.indices[i] = dat.indices[right];
dat.indices[right] = t;
right--;
if (clipR > minb)
clipR = minb;
}
nodeL = std::min(nodeL, minb);
nodeR = std::max(nodeR, maxb);
}
// check for empty space
if (nodeL > nodeBox.lo[axis] && nodeR < nodeBox.hi[axis])
{
float nodeBoxW = nodeBox.hi[axis] - nodeBox.lo[axis];
float nodeNewW = nodeR - nodeL;
// node box is too big compare to space occupied by primitives?
if (1.3f * nodeNewW < nodeBoxW)
{
stats.updateBVH2();
int nextIndex = tempTree.size();
// allocate child
tempTree.push_back(0);
tempTree.push_back(0);
tempTree.push_back(0);
// write bvh2 clip node
stats.updateInner();
tempTree[nodeIndex + 0] = (axis << 30) | (1 << 29) | nextIndex;
tempTree[nodeIndex + 1] = floatToRawIntBits(nodeL);
tempTree[nodeIndex + 2] = floatToRawIntBits(nodeR);
// update nodebox and recurse
nodeBox.lo[axis] = nodeL;
nodeBox.hi[axis] = nodeR;
subdivide(left, rightOrig, tempTree, dat, gridBox, nodeBox, nextIndex, depth + 1, stats);
return;
}
}
// ensure we are making progress in the subdivision
if (right == rightOrig)
{
// all left
if (prevAxis == axis && prevSplit == split) {
// we are stuck here - create a leaf
stats.updateLeaf(depth, right - left + 1);
createNode(tempTree, nodeIndex, left, right);
return;
}
if (clipL <= split) {
// keep looping on left half
gridBox.hi[axis] = split;
prevClip = clipL;
wasLeft = true;
continue;
}
gridBox.hi[axis] = split;
prevClip = G3D::fnan();
}
else if (left > right)
{
// all right
if (prevAxis == axis && prevSplit == split) {
// we are stuck here - create a leaf
stats.updateLeaf(depth, right - left + 1);
createNode(tempTree, nodeIndex, left, right);
return;
}
right = rightOrig;
if (clipR >= split) {
// keep looping on right half
gridBox.lo[axis] = split;
prevClip = clipR;
wasLeft = false;
continue;
}
gridBox.lo[axis] = split;
prevClip = G3D::fnan();
}
else
{
// we are actually splitting stuff
if (prevAxis != -1 && !isnan(prevClip))
{
// second time through - lets create the previous split
// since it produced empty space
int nextIndex = tempTree.size();
// allocate child node
tempTree.push_back(0);
tempTree.push_back(0);
tempTree.push_back(0);
if (wasLeft) {
// create a node with a left child
// write leaf node
stats.updateInner();
tempTree[nodeIndex + 0] = (prevAxis << 30) | nextIndex;
tempTree[nodeIndex + 1] = floatToRawIntBits(prevClip);
tempTree[nodeIndex + 2] = floatToRawIntBits(G3D::inf());
} else {
// create a node with a right child
// write leaf node
stats.updateInner();
tempTree[nodeIndex + 0] = (prevAxis << 30) | (nextIndex - 3);
tempTree[nodeIndex + 1] = floatToRawIntBits(-G3D::inf());
tempTree[nodeIndex + 2] = floatToRawIntBits(prevClip);
}
// count stats for the unused leaf
depth++;
stats.updateLeaf(depth, 0);
// now we keep going as we are, with a new nodeIndex:
nodeIndex = nextIndex;
}
break;
}
}
// compute index of child nodes
int nextIndex = tempTree.size();
// allocate left node
int nl = right - left + 1;
int nr = rightOrig - (right + 1) + 1;
if (nl > 0) {
tempTree.push_back(0);
tempTree.push_back(0);
tempTree.push_back(0);
} else
nextIndex -= 3;
// allocate right node
if (nr > 0) {
tempTree.push_back(0);
tempTree.push_back(0);
tempTree.push_back(0);
}
// write leaf node
stats.updateInner();
tempTree[nodeIndex + 0] = (axis << 30) | nextIndex;
tempTree[nodeIndex + 1] = floatToRawIntBits(clipL);
tempTree[nodeIndex + 2] = floatToRawIntBits(clipR);
// prepare L/R child boxes
AABound gridBoxL(gridBox), gridBoxR(gridBox);
AABound nodeBoxL(nodeBox), nodeBoxR(nodeBox);
gridBoxL.hi[axis] = gridBoxR.lo[axis] = split;
nodeBoxL.hi[axis] = clipL;
nodeBoxR.lo[axis] = clipR;
// recurse
if (nl > 0)
subdivide(left, right, tempTree, dat, gridBoxL, nodeBoxL, nextIndex, depth + 1, stats);
else
stats.updateLeaf(depth + 1, 0);
if (nr > 0)
subdivide(right + 1, rightOrig, tempTree, dat, gridBoxR, nodeBoxR, nextIndex + 3, depth + 1, stats);
else
stats.updateLeaf(depth + 1, 0);
}
bool BIH::writeToFile(FILE *wf) const
{
uint32 treeSize = tree.size();
uint32 check=0, count=0;
check += fwrite(&bounds.low(), sizeof(float), 3, wf);
check += fwrite(&bounds.high(), sizeof(float), 3, wf);
check += fwrite(&treeSize, sizeof(uint32), 1, wf);
check += fwrite(&tree[0], sizeof(uint32), treeSize, wf);
count = objects.size();
check += fwrite(&count, sizeof(uint32), 1, wf);
check += fwrite(&objects[0], sizeof(uint32), count, wf);
return check == (3 + 3 + 2 + treeSize + count);
}
bool BIH::readFromFile(FILE *rf)
{
uint32 treeSize;
Vector3 lo, hi;
uint32 check=0, count=0;
check += fread(&lo, sizeof(float), 3, rf);
check += fread(&hi, sizeof(float), 3, rf);
bounds = AABox(lo, hi);
check += fread(&treeSize, sizeof(uint32), 1, rf);
tree.resize(treeSize);
check += fread(&tree[0], sizeof(uint32), treeSize, rf);
check += fread(&count, sizeof(uint32), 1, rf);
objects.resize(count); // = new uint32[nObjects];
check += fread(&objects[0], sizeof(uint32), count, rf);
return check == (3 + 3 + 2 + treeSize + count);
}
void BIH::BuildStats::updateLeaf(int depth, int n)
{
numLeaves++;
minDepth = std::min(depth, minDepth);
maxDepth = std::max(depth, maxDepth);
sumDepth += depth;
minObjects = std::min(n, minObjects);
maxObjects = std::max(n, maxObjects);
sumObjects += n;
int nl = std::min(n, 5);
++numLeavesN[nl];
}
void BIH::BuildStats::printStats()
{
printf("Tree stats:\n");
printf(" * Nodes: %d\n", numNodes);
printf(" * Leaves: %d\n", numLeaves);
printf(" * Objects: min %d\n", minObjects);
printf(" avg %.2f\n", (float) sumObjects / numLeaves);
printf(" avg(n>0) %.2f\n", (float) sumObjects / (numLeaves - numLeavesN[0]));
printf(" max %d\n", maxObjects);
printf(" * Depth: min %d\n", minDepth);
printf(" avg %.2f\n", (float) sumDepth / numLeaves);
printf(" max %d\n", maxDepth);
printf(" * Leaves w/: N=0 %3d%%\n", 100 * numLeavesN[0] / numLeaves);
printf(" N=1 %3d%%\n", 100 * numLeavesN[1] / numLeaves);
printf(" N=2 %3d%%\n", 100 * numLeavesN[2] / numLeaves);
printf(" N=3 %3d%%\n", 100 * numLeavesN[3] / numLeaves);
printf(" N=4 %3d%%\n", 100 * numLeavesN[4] / numLeaves);
printf(" N>4 %3d%%\n", 100 * numLeavesN[5] / numLeaves);
printf(" * BVH2 nodes: %d (%3d%%)\n", numBVH2, 100 * numBVH2 / (numNodes + numLeaves - 2 * numBVH2));
}
@@ -1,391 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* 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.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _BIH_H
#define _BIH_H
#include <G3D/Vector3.h>
#include <G3D/Ray.h>
#include <G3D/AABox.h>
#include <Platform/Define.h>
#include <stdexcept>
#include <vector>
#include <algorithm>
#include <limits>
#include <cmath>
#define MAX_STACK_SIZE 64
#ifdef _MSC_VER
#define isnan(x) _isnan(x)
#endif
using G3D::Vector3;
using G3D::AABox;
using G3D::Ray;
static inline uint32 floatToRawIntBits(float f)
{
union
{
uint32 ival;
float fval;
} temp;
temp.fval=f;
return temp.ival;
}
static inline float intBitsToFloat(uint32 i)
{
union
{
uint32 ival;
float fval;
} temp;
temp.ival=i;
return temp.fval;
}
struct AABound
{
Vector3 lo, hi;
};
/** Bounding Interval Hierarchy Class.
Building and Ray-Intersection functions based on BIH from
Sunflow, a Java Raytracer, released under MIT/X11 License
http://sunflow.sourceforge.net/
Copyright (c) 2003-2007 Christopher Kulla
*/
class BIH
{
public:
BIH() {};
template< class T, class BoundsFunc >
void build(const std::vector<T> &primitives, BoundsFunc &getBounds, uint32 leafSize = 3, bool printStats=false)
{
if(primitives.size() == 0)
return;
buildData dat;
dat.maxPrims = leafSize;
dat.numPrims = primitives.size();
dat.indices = new uint32[dat.numPrims];
dat.primBound = new AABox[dat.numPrims];
getBounds(primitives[0], bounds);
for (uint32 i=0; i<dat.numPrims; ++i)
{
dat.indices[i] = i;
AABox tb;
getBounds(primitives[i], dat.primBound[i]);
bounds.merge(dat.primBound[i]);
}
std::vector<uint32> tempTree;
BuildStats stats;
buildHierarchy(tempTree, dat, stats);
if (printStats)
stats.printStats();
objects.resize(dat.numPrims);
for (uint32 i=0; i<dat.numPrims; ++i)
objects[i] = dat.indices[i];
//nObjects = dat.numPrims;
tree = tempTree;
delete[] dat.primBound;
delete[] dat.indices;
}
uint32 primCount() { return objects.size(); }
template<typename RayCallback>
void intersectRay(const Ray &r, RayCallback& intersectCallback, float &maxDist, bool stopAtFirst=false) const
{
float intervalMin = 0.f;
float intervalMax = maxDist;
Vector3 org = r.origin();
Vector3 dir = r.direction();
Vector3 invDir;
float t1, t2;
for(int i=0; i<3; ++i)
{
invDir[i] = 1.f / dir[i];
t1 = (bounds.low()[i] - org[i]) * invDir[i];
t2 = (bounds.high()[i] - org[i]) * invDir[i];
if (invDir[i] > 0) {
if (t1 > intervalMin)
intervalMin = t1;
if (t2 < intervalMax)
intervalMax = t2;
} else {
if (t2 > intervalMin)
intervalMin = t2;
if (t1 < intervalMax)
intervalMax = t1;
}
if (intervalMin > intervalMax)
return;
}
uint32 offsetFront[3];
uint32 offsetBack[3];
uint32 offsetFront3[3];
uint32 offsetBack3[3];
// compute custom offsets from direction sign bit
for(int i=0; i<3; ++i)
{
offsetFront[i] = floatToRawIntBits(dir[i]) >> 31;
offsetBack[i] = offsetFront[i] ^ 1;
offsetFront3[i] = offsetFront[i] * 3;
offsetBack3[i] = offsetBack[i] * 3;
// avoid always adding 1 during the inner loop
++offsetFront[i];
++offsetBack[i];
}
StackNode stack[MAX_STACK_SIZE];
int stackPos = 0;
int node = 0;
while (true) {
while (true)
{
uint32 tn = tree[node];
uint32 axis = (tn & (3 << 30)) >> 30;
bool BVH2 = tn & (1 << 29);
int offset = tn & ~(7 << 29);
if (!BVH2)
{
if (axis < 3)
{
// "normal" interior node
float tf = (intBitsToFloat(tree[node + offsetFront[axis]]) - org[axis]) * invDir[axis];
float tb = (intBitsToFloat(tree[node + offsetBack[axis]]) - org[axis]) * invDir[axis];
// ray passes between clip zones
if (tf < intervalMin && tb > intervalMax)
break;
int back = offset + offsetBack3[axis];
node = back;
// ray passes through far node only
if (tf < intervalMin) {
intervalMin = (tb >= intervalMin) ? tb : intervalMin;
continue;
}
node = offset + offsetFront3[axis]; // front
// ray passes through near node only
if (tb > intervalMax) {
intervalMax = (tf <= intervalMax) ? tf : intervalMax;
continue;
}
// ray passes through both nodes
// push back node
stack[stackPos].node = back;
stack[stackPos].tnear = (tb >= intervalMin) ? tb : intervalMin;
stack[stackPos].tfar = intervalMax;
stackPos++;
// update ray interval for front node
intervalMax = (tf <= intervalMax) ? tf : intervalMax;
continue;
}
else
{
// leaf - test some objects
int n = tree[node + 1];
while (n > 0) {
bool hit = intersectCallback(r, objects[offset], maxDist, stopAtFirst);
if(stopAtFirst && hit) return;
--n;
++offset;
}
break;
}
}
else
{
if (axis>2)
return; // should not happen
float tf = (intBitsToFloat(tree[node + offsetFront[axis]]) - org[axis]) * invDir[axis];
float tb = (intBitsToFloat(tree[node + offsetBack[axis]]) - org[axis]) * invDir[axis];
node = offset;
intervalMin = (tf >= intervalMin) ? tf : intervalMin;
intervalMax = (tb <= intervalMax) ? tb : intervalMax;
if (intervalMin > intervalMax)
break;
continue;
}
} // traversal loop
do
{
// stack is empty?
if (stackPos == 0)
return;
// move back up the stack
stackPos--;
intervalMin = stack[stackPos].tnear;
if (maxDist < intervalMin)
continue;
node = stack[stackPos].node;
intervalMax = stack[stackPos].tfar;
break;
} while (true);
}
}
template<typename IsectCallback>
void intersectPoint(const Vector3 &p, IsectCallback& intersectCallback) const
{
if (!bounds.contains(p))
return;
StackNode stack[MAX_STACK_SIZE];
int stackPos = 0;
int node = 0;
while (true) {
while (true)
{
uint32 tn = tree[node];
uint32 axis = (tn & (3 << 30)) >> 30;
bool BVH2 = tn & (1 << 29);
int offset = tn & ~(7 << 29);
if (!BVH2)
{
if (axis < 3)
{
// "normal" interior node
float tl = intBitsToFloat(tree[node + 1]);
float tr = intBitsToFloat(tree[node + 2]);
// point is between clip zones
if (tl < p[axis] && tr > p[axis])
break;
int right = offset + 3;
node = right;
// point is in right node only
if (tl < p[axis]) {
continue;
}
node = offset; // left
// point is in left node only
if (tr > p[axis]) {
continue;
}
// point is in both nodes
// push back right node
stack[stackPos].node = right;
stackPos++;
continue;
}
else
{
// leaf - test some objects
int n = tree[node + 1];
while (n > 0) {
intersectCallback(p, objects[offset]); // !!!
--n;
++offset;
}
break;
}
}
else // BVH2 node (empty space cut off left and right)
{
if (axis>2)
return; // should not happen
float tl = intBitsToFloat(tree[node + 1]);
float tr = intBitsToFloat(tree[node + 2]);
node = offset;
if (tl > p[axis] || tr < p[axis])
break;
continue;
}
} // traversal loop
// stack is empty?
if (stackPos == 0)
return;
// move back up the stack
stackPos--;
node = stack[stackPos].node;
}
}
bool writeToFile(FILE *wf) const;
bool readFromFile(FILE *rf);
protected:
std::vector<uint32> tree;
std::vector<uint32> objects;
AABox bounds;
struct buildData
{
uint32 *indices;
AABox *primBound;
uint32 numPrims;
int maxPrims;
};
struct StackNode
{
uint32 node;
float tnear;
float tfar;
};
class BuildStats
{
private:
int numNodes;
int numLeaves;
int sumObjects;
int minObjects;
int maxObjects;
int sumDepth;
int minDepth;
int maxDepth;
int numLeavesN[6];
int numBVH2;
public:
BuildStats():
numNodes(0), numLeaves(0), sumObjects(0), minObjects(0x0FFFFFFF),
maxObjects(0xFFFFFFFF), sumDepth(0), minDepth(0x0FFFFFFF),
maxDepth(0xFFFFFFFF), numBVH2(0)
{
for(int i=0; i<6; ++i) numLeavesN[i] = 0;
}
void updateInner() { numNodes++; }
void updateBVH2() { numBVH2++; }
void updateLeaf(int depth, int n);
void printStats();
};
void buildHierarchy(std::vector<uint32> &tempTree, buildData &dat, BuildStats &stats);
void createNode(std::vector<uint32> &tempTree, int nodeIndex, uint32 left, uint32 right) {
// write leaf node
tempTree[nodeIndex + 0] = (3 << 30) | left;
tempTree[nodeIndex + 1] = right - left + 1;
}
void subdivide(int left, int right, std::vector<uint32> &tempTree, buildData &dat, AABound &gridBox, AABound &nodeBox, int nodeIndex, int depth, BuildStats &stats);
};
#endif // _BIH_H
@@ -1,30 +0,0 @@
########### next target ###############
SET(vmaps_STAT_SRCS
BIH.h
BIH.cpp
IVMapManager.h
MapTree.cpp
MapTree.h
ModelInstance.cpp
ModelInstance.h
TileAssembler.cpp
TileAssembler.h
VMapDefinitions.h
VMapFactory.cpp
VMapFactory.h
VMapManager2.cpp
VMapManager2.h
VMapTools.h
WorldModel.cpp
WorldModel.h
)
include_directories(
${ACE_INCLUDE_DIR}
${CMAKE_SOURCE_DIR}/src/server/framework
${CMAKE_SOURCE_DIR}/dep/include/g3dlite
)
add_library(vmaps STATIC ${vmaps_STAT_SRCS})
@@ -1,106 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* 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.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _IVMAPMANAGER_H
#define _IVMAPMANAGER_H
#include<string>
#include <Platform/Define.h>
//===========================================================
/**
This is the minimum interface to the VMapMamager.
*/
namespace VMAP
{
enum VMAP_LOAD_RESULT
{
VMAP_LOAD_RESULT_ERROR,
VMAP_LOAD_RESULT_OK,
VMAP_LOAD_RESULT_IGNORED,
};
#define VMAP_INVALID_HEIGHT -100000.0f // for check
#define VMAP_INVALID_HEIGHT_VALUE -200000.0f // real assigned value in unknown height case
//===========================================================
class IVMapManager
{
private:
bool iEnableLineOfSightCalc;
bool iEnableHeightCalc;
public:
IVMapManager() : iEnableLineOfSightCalc(true), iEnableHeightCalc(true) {}
virtual ~IVMapManager(void) {}
virtual int loadMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0;
virtual bool existsMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0;
virtual void unloadMap(unsigned int pMapId, int x, int y) = 0;
virtual void unloadMap(unsigned int pMapId) = 0;
virtual bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2) = 0;
virtual float getHeight(unsigned int pMapId, float x, float y, float z) = 0;
/**
test if we hit an object. return true if we hit one. rx,ry,rz will hold the hit position or the dest position, if no intersection was found
return a position, that is pReduceDist closer to the origin
*/
virtual bool getObjectHitPos(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float pModifyDist) = 0;
/**
send debug commands
*/
virtual bool processCommand(char *pCommand)= 0;
/**
Enable/disable LOS calculation
It is enabled by default. If it is enabled in mid game the maps have to loaded manualy
*/
void setEnableLineOfSightCalc(bool pVal) { iEnableLineOfSightCalc = pVal; }
/**
Enable/disable model height calculation
It is enabled by default. If it is enabled in mid game the maps have to loaded manualy
*/
void setEnableHeightCalc(bool pVal) { iEnableHeightCalc = pVal; }
bool isLineOfSightCalcEnabled() const { return(iEnableLineOfSightCalc); }
bool isHeightCalcEnabled() const { return(iEnableHeightCalc); }
bool isMapLoadingEnabled() const { return(iEnableLineOfSightCalc || iEnableHeightCalc ); }
virtual std::string getDirFileName(unsigned int pMapId, int x, int y) const =0;
/**
Block maps from being used.
parameter: String of map ids. Delimiter = ","
e.g.: "0,1,530"
*/
virtual void preventMapsFromBeingUsed(const char* pMapIdString) =0;
/**
Query world model area info.
\param z gets adjusted to the ground height for which this are info is valid
*/
virtual bool getAreaInfo(unsigned int pMapId, float x, float y, float &z, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const=0;
virtual bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 ReqLiquidType, float &level, float &floor, uint32 &type) const=0;
};
}
#endif
@@ -1,450 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* 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.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "MapTree.h"
#include "ModelInstance.h"
#include "VMapManager2.h"
#include "VMapDefinitions.h"
#include <string>
#include <sstream>
#include <iomanip>
using G3D::Vector3;
namespace VMAP
{
class MapRayCallback
{
public:
MapRayCallback(ModelInstance *val): prims(val) {}
ModelInstance *prims;
bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool pStopAtFirstHit=true)
{
return prims[entry].intersectRay(ray, distance, pStopAtFirstHit);
//std::cout << "trying to intersect '" << entity->name << "'\n";
}
};
class AreaInfoCallback
{
public:
AreaInfoCallback(ModelInstance *val): prims(val) {}
void operator()(const Vector3& point, uint32 entry)
{
#ifdef VMAP_DEBUG
std::cout << "trying to intersect '" << prims[entry].name << "'\n";
#endif
prims[entry].intersectPoint(point, aInfo);
}
ModelInstance *prims;
AreaInfo aInfo;
};
class LocationInfoCallback
{
public:
LocationInfoCallback(ModelInstance *val, LocationInfo &info): prims(val), locInfo(info), result(false) {}
void operator()(const Vector3& point, uint32 entry)
{
#ifdef VMAP_DEBUG
std::cout << "trying to intersect '" << prims[entry].name << "'\n";
#endif
if (prims[entry].GetLocationInfo(point, locInfo))
result = true;
}
ModelInstance *prims;
LocationInfo &locInfo;
bool result;
};
//=========================================================
std::string StaticMapTree::getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY)
{
std::stringstream tilefilename;
tilefilename.fill('0');
tilefilename << std::setw(3) << mapID << "_";
//tilefilename << std::setw(2) << tileX << "_" << std::setw(2) << tileY << ".vmtile";
tilefilename << std::setw(2) << tileY << "_" << std::setw(2) << tileX << ".vmtile";
return tilefilename.str();
}
bool StaticMapTree::getAreaInfo(Vector3 &pos, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const
{
AreaInfoCallback intersectionCallBack(iTreeValues);
iTree.intersectPoint(pos, intersectionCallBack);
if (intersectionCallBack.aInfo.result)
{
flags = intersectionCallBack.aInfo.flags;
adtId = intersectionCallBack.aInfo.adtId;
rootId = intersectionCallBack.aInfo.rootId;
groupId = intersectionCallBack.aInfo.groupId;
pos.z = intersectionCallBack.aInfo.ground_Z;
return true;
}
return false;
}
bool StaticMapTree::GetLocationInfo(const Vector3 &pos, LocationInfo &info) const
{
LocationInfoCallback intersectionCallBack(iTreeValues, info);
iTree.intersectPoint(pos, intersectionCallBack);
return intersectionCallBack.result;
}
StaticMapTree::StaticMapTree(uint32 mapID, const std::string &basePath):
iMapID(mapID), /* iTree(0), */ iTreeValues(0), iBasePath(basePath)
{
if (iBasePath.length() > 0 && (iBasePath[iBasePath.length()-1] != '/' || iBasePath[iBasePath.length()-1] != '\\'))
{
iBasePath.append("/");
}
}
//=========================================================
//! Make sure to call unloadMap() to unregister acquired model references before destroying
StaticMapTree::~StaticMapTree()
{
delete[] iTreeValues;
}
//=========================================================
/**
return dist to hit or inf() if no hit
*/
float StaticMapTree::getIntersectionTime(const G3D::Ray& pRay, float pMaxDist, bool pStopAtFirstHit) const
{
float distance = pMaxDist;
MapRayCallback intersectionCallBack(iTreeValues);
iTree.intersectRay(pRay, intersectionCallBack, distance, pStopAtFirstHit);
return distance;
}
//=========================================================
bool StaticMapTree::isInLineOfSight(const Vector3& pos1, const Vector3& pos2) const
{
bool result = true;
float maxDist = (pos2 - pos1).magnitude();
// prevent NaN values which can cause BIH intersection to enter infinite loop
if (maxDist < 1e-10f)
return true;
// direction with length of 1
G3D::Ray ray = G3D::Ray::fromOriginAndDirection(pos1, (pos2 - pos1)/maxDist);
float resultDist = getIntersectionTime(ray, maxDist, true);
if (resultDist < maxDist)
{
result = false;
}
return result;
}
//=========================================================
/**
When moving from pos1 to pos2 check if we hit an object. Return true and the position if we hit one
Return the hit pos or the original dest pos
*/
bool StaticMapTree::getObjectHitPos(const Vector3& pPos1, const Vector3& pPos2, Vector3& pResultHitPos, float pModifyDist) const
{
bool result=false;
float maxDist = (pPos2 - pPos1).magnitude();
// prevent NaN values which can cause BIH intersection to enter infinite loop
if (maxDist < 1e-10f)
{
pResultHitPos = pPos2;
return false;
}
Vector3 dir = (pPos2 - pPos1)/maxDist; // direction with length of 1
G3D::Ray ray(pPos1, dir);
float dist = getIntersectionTime(ray, maxDist, false);
if (dist < maxDist)
{
pResultHitPos = pPos1 + dir * dist;
if (pModifyDist < 0)
{
if ((pResultHitPos - pPos1).magnitude() > -pModifyDist)
{
pResultHitPos = pResultHitPos + dir*pModifyDist;
}
else
{
pResultHitPos = pPos1;
}
}
else
{
pResultHitPos = pResultHitPos + dir*pModifyDist;
}
result = true;
}
else
{
pResultHitPos = pPos2;
result = false;
}
return result;
}
//=========================================================
float StaticMapTree::getHeight(const Vector3& pPos) const
{
float height = G3D::inf();
Vector3 dir = Vector3(0,0,-1);
G3D::Ray ray(pPos, dir); // direction with length of 1
float maxDist = VMapDefinitions::getMaxCanFallDistance();
float dist = getIntersectionTime(ray, maxDist, false);
if (dist < maxDist)
{
height = pPos.z - dist;
}
return(height);
}
//=========================================================
bool StaticMapTree::CanLoadMap(const std::string &vmapPath, uint32 mapID, uint32 tileX, uint32 tileY)
{
std::string basePath = vmapPath;
if (basePath.length() > 0 && (basePath[basePath.length()-1] != '/' || basePath[basePath.length()-1] != '\\'))
basePath.append("/");
std::string fullname = basePath + VMapManager2::getMapFileName(mapID);
bool success = true;
FILE *rf = fopen(fullname.c_str(), "rb");
if (!rf)
return false;
// TODO: check magic number when implemented...
char tiled;
char chunk[8];
if (!readChunk(rf, chunk, VMAP_MAGIC, 8) || fread(&tiled, sizeof(char), 1, rf) != 1)
{
fclose(rf);
return false;
}
if (tiled)
{
std::string tilefile = basePath + getTileFileName(mapID, tileX, tileY);
FILE* tf = fopen(tilefile.c_str(), "rb");
if (!tf)
success = false;
else
fclose(tf);
}
fclose(rf);
return success;
}
//=========================================================
bool StaticMapTree::InitMap(const std::string &fname, VMapManager2 *vm)
{
std::cout << "Initializing StaticMapTree '" << fname << "'\n";
bool success = true;
std::string fullname = iBasePath + fname;
FILE *rf = fopen(fullname.c_str(), "rb");
if (!rf)
return false;
else
{
char chunk[8];
//general info
if (!readChunk(rf, chunk, VMAP_MAGIC, 8)) success = false;
char tiled;
if (success && fread(&tiled, sizeof(char), 1, rf) != 1) success = false;
iIsTiled = bool(tiled);
// Nodes
if (success && !readChunk(rf, chunk, "NODE", 4)) success = false;
if (success) success = iTree.readFromFile(rf);
if (success)
{
iNTreeValues = iTree.primCount();
iTreeValues = new ModelInstance[iNTreeValues];
}
if (success && !readChunk(rf, chunk, "GOBJ", 4)) success = false;
// global model spawns
// only non-tiled maps have them, and if so exactly one (so far at least...)
ModelSpawn spawn;
#ifdef VMAP_DEBUG
std::cout << "Map isTiled:" << bool(iIsTiled) << std::endl;
#endif
if (!iIsTiled && ModelSpawn::readFromFile(rf, spawn))
{
WorldModel *model = vm->acquireModelInstance(iBasePath, spawn.name);
std::cout << "StaticMapTree::InitMap(): loading " << spawn.name << std::endl;
if (model)
{
// assume that global model always is the first and only tree value (could be improved...)
iTreeValues[0] = ModelInstance(spawn, model);
iLoadedSpawns[0] = 1;
}
else
{
success = false;
std::cout << "error: could not acquire WorldModel pointer!\n";
}
}
fclose(rf);
}
return success;
}
//=========================================================
void StaticMapTree::UnloadMap(VMapManager2 *vm)
{
for (loadedSpawnMap::iterator i = iLoadedSpawns.begin(); i != iLoadedSpawns.end(); ++i)
{
iTreeValues[i->first].setUnloaded();
for (uint32 refCount = 0; refCount < i->second; ++refCount)
vm->releaseModelInstance(iTreeValues[i->first].name);
}
iLoadedSpawns.clear();
iLoadedTiles.clear();
}
//=========================================================
bool StaticMapTree::LoadMapTile(uint32 tileX, uint32 tileY, VMapManager2 *vm)
{
if (!iIsTiled)
{
// currently, core creates grids for all maps, whether it has terrain tiles or not
// so we need "fake" tile loads to know when we can unload map geometry
iLoadedTiles[packTileID(tileX, tileY)] = false;
return true;
}
if (!iTreeValues)
{
std::cout << "Tree has not been initialized!\n";
return false;
}
bool result = true;
std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY);
FILE* tf = fopen(tilefile.c_str(), "rb");
if (tf)
{
uint32 numSpawns;
if (fread(&numSpawns, sizeof(uint32), 1, tf) != 1)
result = false;
for (uint32 i=0; i<numSpawns && result; ++i)
{
// read model spawns
ModelSpawn spawn;
result = ModelSpawn::readFromFile(tf, spawn);
if (result)
{
// acquire model instance
WorldModel *model = vm->acquireModelInstance(iBasePath, spawn.name);
if (!model) std::cout << "error: could not acquire WorldModel pointer!\n";
// update tree
uint32 referencedVal;
fread(&referencedVal, sizeof(uint32), 1, tf);
if (!iLoadedSpawns.count(referencedVal))
{
#ifdef VMAP_DEBUG
if (referencedVal > iNTreeValues)
{
std::cout << "invalid tree element! (" << referencedVal << "/" << iNTreeValues << ")\n";
continue;
}
#endif
iTreeValues[referencedVal] = ModelInstance(spawn, model);
iLoadedSpawns[referencedVal] = 1;
}
else
{
++iLoadedSpawns[referencedVal];
#ifdef VMAP_DEBUG
if (iTreeValues[referencedVal].ID != spawn.ID) std::cout << "error: trying to load wrong spawn in node!\n";
else if (iTreeValues[referencedVal].name != spawn.name) std::cout << "error: name collision on GUID="<< spawn.ID << "\n";
#endif
}
}
}
iLoadedTiles[packTileID(tileX, tileY)] = true;
fclose(tf);
}
else
iLoadedTiles[packTileID(tileX, tileY)] = false;
return result;
}
//=========================================================
void StaticMapTree::UnloadMapTile(uint32 tileX, uint32 tileY, VMapManager2 *vm)
{
uint32 tileID = packTileID(tileX, tileY);
loadedTileMap::iterator tile = iLoadedTiles.find(tileID);
if (tile == iLoadedTiles.end())
{
std::cout << "WARNING: trying to unload non-loaded tile. Map:" << iMapID << " X:" << tileX << " Y:" << tileY << std::endl;
return;
}
if (tile->second) // file associated with tile
{
std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY);
FILE* tf = fopen(tilefile.c_str(), "rb");
if (tf)
{
bool result=true;
uint32 numSpawns;
if (fread(&numSpawns, sizeof(uint32), 1, tf) != 1)
result = false;
for (uint32 i=0; i<numSpawns && result; ++i)
{
// read model spawns
ModelSpawn spawn;
result = ModelSpawn::readFromFile(tf, spawn);
if (result)
{
// release model instance
vm->releaseModelInstance(spawn.name);
// update tree
uint32 referencedNode;
fread(&referencedNode, sizeof(uint32), 1, tf);
if (!iLoadedSpawns.count(referencedNode))
{
std::cout << "error! trying to unload non-referenced model '" << spawn.name << "' (ID:" << spawn.ID << ")\n";
}
else if (--iLoadedSpawns[referencedNode] == 0)
{
//std::cout << "MapTree: removing '" << spawn.name << "' from tree\n";
iTreeValues[referencedNode].setUnloaded();
iLoadedSpawns.erase(referencedNode);
}
}
}
fclose(tf);
}
}
iLoadedTiles.erase(tile);
}
}
@@ -1,97 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* 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.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _MAPTREE_H
#define _MAPTREE_H
#include "Platform/Define.h"
#include "Utilities/UnorderedMap.h"
#include "BIH.h"
namespace VMAP
{
class ModelInstance;
class GroupModel;
class VMapManager2;
struct LocationInfo
{
LocationInfo(): hitInstance(0), hitModel(0), ground_Z(-G3D::inf()) {};
const ModelInstance *hitInstance;
const GroupModel *hitModel;
float ground_Z;
};
class StaticMapTree
{
typedef UNORDERED_MAP<uint32, bool> loadedTileMap;
typedef UNORDERED_MAP<uint32, uint32> loadedSpawnMap;
private:
uint32 iMapID;
bool iIsTiled;
BIH iTree;
ModelInstance *iTreeValues; // the tree entries
uint32 iNTreeValues;
// Store all the map tile idents that are loaded for that map
// some maps are not splitted into tiles and we have to make sure, not removing the map before all tiles are removed
// empty tiles have no tile file, hence map with bool instead of just a set (consistency check)
loadedTileMap iLoadedTiles;
// stores <tree_index, reference_count> to invalidate tree values, unload map, and to be able to report errors
loadedSpawnMap iLoadedSpawns;
std::string iBasePath;
private:
float getIntersectionTime(const G3D::Ray& pRay, float pMaxDist, bool pStopAtFirstHit) const;
//bool containsLoadedMapTile(unsigned int pTileIdent) const { return(iLoadedMapTiles.containsKey(pTileIdent)); }
public:
static std::string getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY);
static uint32 packTileID(uint32 tileX, uint32 tileY) { return tileX<<16 | tileY; }
static void unpackTileID(uint32 ID, uint32 &tileX, uint32 &tileY) { tileX = ID>>16; tileY = ID&0xFF; }
static bool CanLoadMap(const std::string &basePath, uint32 mapID, uint32 tileX, uint32 tileY);
StaticMapTree(uint32 mapID, const std::string &basePath);
~StaticMapTree();
bool isInLineOfSight(const G3D::Vector3& pos1, const G3D::Vector3& pos2) const;
bool getObjectHitPos(const G3D::Vector3& pos1, const G3D::Vector3& pos2, G3D::Vector3& pResultHitPos, float pModifyDist) const;
float getHeight(const G3D::Vector3& pPos) const;
bool getAreaInfo(G3D::Vector3 &pos, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const;
bool GetLocationInfo(const Vector3 &pos, LocationInfo &info) const;
bool InitMap(const std::string &fname, VMapManager2 *vm);
void UnloadMap(VMapManager2 *vm);
bool LoadMapTile(uint32 tileX, uint32 tileY, VMapManager2 *vm);
void UnloadMapTile(uint32 tileX, uint32 tileY, VMapManager2 *vm);
bool isTiled() const { return iIsTiled; }
uint32 numLoadedTiles() const { return iLoadedTiles.size(); }
};
struct AreaInfo
{
AreaInfo(): result(false), ground_Z(-G3D::inf()) {};
bool result;
float ground_Z;
uint32 flags;
int32 adtId;
int32 rootId;
int32 groupId;
};
} // VMAP
#endif // _MAPTREE_H
@@ -1,219 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* 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.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ModelInstance.h"
#include "WorldModel.h"
#include "MapTree.h"
using G3D::Vector3;
using G3D::Ray;
namespace VMAP
{
ModelInstance::ModelInstance(const ModelSpawn &spawn, WorldModel *model): ModelSpawn(spawn), iModel(model)
{
iInvRot = G3D::Matrix3::fromEulerAnglesZYX(G3D::pi()*iRot.y/180.f, G3D::pi()*iRot.x/180.f, G3D::pi()*iRot.z/180.f).inverse();
iInvScale = 1.f/iScale;
}
bool ModelInstance::intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit) const
{
if (!iModel)
{
//std::cout << "<object not loaded>\n";
return false;
}
float time = pRay.intersectionTime(iBound);
if (time == G3D::inf())
{
// std::cout << "Ray does not hit '" << name << "'\n";
return false;
}
// std::cout << "Ray crosses bound of '" << name << "'\n";
/* std::cout << "ray from:" << pRay.origin().x << ", " << pRay.origin().y << ", " << pRay.origin().z
<< " dir:" << pRay.direction().x << ", " << pRay.direction().y << ", " << pRay.direction().z
<< " t/tmax:" << time << "/" << pMaxDist;
std::cout << "\nBound lo:" << iBound.low().x << ", " << iBound.low().y << ", " << iBound.low().z << " hi: "
<< iBound.high().x << ", " << iBound.high().y << ", " << iBound.high().z << std::endl; */
// child bounds are defined in object space:
Vector3 p = iInvRot * (pRay.origin() - iPos) * iInvScale;
Ray modRay(p, iInvRot * pRay.direction());
float distance = pMaxDist * iInvScale;
bool hit = iModel->IntersectRay(modRay, distance, pStopAtFirstHit);
distance *= iScale;
pMaxDist = distance;
return hit;
}
void ModelInstance::intersectPoint(const G3D::Vector3& p, AreaInfo &info) const
{
if (!iModel)
{
#ifdef VMAP_DEBUG
std::cout << "<object not loaded>\n";
#endif
return;
}
// M2 files don't contain area info, only WMO files
if (flags & MOD_M2)
return;
if (!iBound.contains(p))
return;
// child bounds are defined in object space:
Vector3 pModel = iInvRot * (p - iPos) * iInvScale;
Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
float zDist;
if (iModel->IntersectPoint(pModel, zDirModel, zDist, info))
{
Vector3 modelGround = pModel + zDist * zDirModel;
// Transform back to world space. Note that:
// Mat * vec == vec * Mat.transpose()
// and for rotation matrices: Mat.inverse() == Mat.transpose()
float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
if (info.ground_Z < world_Z)
{
info.ground_Z = world_Z;
info.adtId = adtId;
}
}
}
bool ModelInstance::GetLocationInfo(const G3D::Vector3& p, LocationInfo &info) const
{
if (!iModel)
{
#ifdef VMAP_DEBUG
std::cout << "<object not loaded>\n";
#endif
return false;
}
// M2 files don't contain area info, only WMO files
if (flags & MOD_M2)
return false;
if (!iBound.contains(p))
return false;
// child bounds are defined in object space:
Vector3 pModel = iInvRot * (p - iPos) * iInvScale;
Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
float zDist;
if (iModel->GetLocationInfo(pModel, zDirModel, zDist, info))
{
Vector3 modelGround = pModel + zDist * zDirModel;
// Transform back to world space. Note that:
// Mat * vec == vec * Mat.transpose()
// and for rotation matrices: Mat.inverse() == Mat.transpose()
float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
if (info.ground_Z < world_Z) // hm...could it be handled automatically with zDist at intersection?
{
info.ground_Z = world_Z;
info.hitInstance = this;
return true;
}
}
return false;
}
bool ModelInstance::GetLiquidLevel(const G3D::Vector3& p, LocationInfo &info, float &liqHeight) const
{
// child bounds are defined in object space:
Vector3 pModel = iInvRot * (p - iPos) * iInvScale;
//Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
float zDist;
if (info.hitModel->GetLiquidLevel(pModel, zDist))
{
// calculate world height (zDist in model coords):
// assume WMO not tilted (wouldn't make much sense anyway)
liqHeight = zDist * iScale + iPos.z;
return true;
}
return false;
}
bool ModelSpawn::readFromFile(FILE *rf, ModelSpawn &spawn)
{
uint32 check=0, nameLen;
check += fread(&spawn.flags, sizeof(uint32), 1, rf);
// EoF?
if (!check)
{
if (ferror(rf))
std::cout << "Error reading ModelSpawn!\n";
return false;
}
check += fread(&spawn.adtId, sizeof(uint16), 1, rf);
check += fread(&spawn.ID, sizeof(uint32), 1, rf);
check += fread(&spawn.iPos, sizeof(float), 3, rf);
check += fread(&spawn.iRot, sizeof(float), 3, rf);
check += fread(&spawn.iScale, sizeof(float), 1, rf);
bool has_bound = (spawn.flags & MOD_HAS_BOUND);
if (has_bound) // only WMOs have bound in MPQ, only available after computation
{
Vector3 bLow, bHigh;
check += fread(&bLow, sizeof(float), 3, rf);
check += fread(&bHigh, sizeof(float), 3, rf);
spawn.iBound = G3D::AABox(bLow, bHigh);
}
check += fread(&nameLen, sizeof(uint32), 1, rf);
if(check != (has_bound ? 17 : 11))
{
std::cout << "Error reading ModelSpawn!\n";
return false;
}
char nameBuff[500];
if (nameLen>500) // file names should never be that long, must be file error
{
std::cout << "Error reading ModelSpawn, file name too long!\n";
return false;
}
check = fread(nameBuff, sizeof(char), nameLen, rf);
if (check != nameLen)
{
std::cout << "Error reading ModelSpawn!\n";
return false;
}
spawn.name = std::string(nameBuff, nameLen);
return true;
}
bool ModelSpawn::writeToFile(FILE *wf, const ModelSpawn &spawn)
{
uint32 check=0;
check += fwrite(&spawn.flags, sizeof(uint32), 1, wf);
check += fwrite(&spawn.adtId, sizeof(uint16), 1, wf);
check += fwrite(&spawn.ID, sizeof(uint32), 1, wf);
check += fwrite(&spawn.iPos, sizeof(float), 3, wf);
check += fwrite(&spawn.iRot, sizeof(float), 3, wf);
check += fwrite(&spawn.iScale, sizeof(float), 1, wf);
bool has_bound = (spawn.flags & MOD_HAS_BOUND);
if(has_bound) // only WMOs have bound in MPQ, only available after computation
{
check += fwrite(&spawn.iBound.low(), sizeof(float), 3, wf);
check += fwrite(&spawn.iBound.high(), sizeof(float), 3, wf);
}
uint32 nameLen = spawn.name.length();
check += fwrite(&nameLen, sizeof(uint32), 1, wf);
if(check != (has_bound ? 17 : 11)) return false;
check = fwrite(spawn.name.c_str(), sizeof(char), nameLen, wf);
if(check != nameLen) return false;
return true;
}
}
@@ -1,81 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* 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.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _MODELINSTANCE_H_
#define _MODELINSTANCE_H_
#include <G3D/Matrix3.h>
#include <G3D/Vector3.h>
#include <G3D/AABox.h>
#include <G3D/Ray.h>
#include "Platform/Define.h"
namespace VMAP
{
class WorldModel;
struct AreaInfo;
struct LocationInfo;
enum ModelFlags
{
MOD_M2 = 1,
MOD_WORLDSPAWN = 1<<1,
MOD_HAS_BOUND = 1<<2
};
class ModelSpawn
{
public:
//mapID, tileX, tileY, Flags, ID, Pos, Rot, Scale, Bound_lo, Bound_hi, name
uint32 flags;
uint16 adtId;
uint32 ID;
G3D::Vector3 iPos;
G3D::Vector3 iRot;
float iScale;
G3D::AABox iBound;
std::string name;
bool operator==(const ModelSpawn &other) const { return ID == other.ID; }
//uint32 hashCode() const { return ID; }
// temp?
const G3D::AABox& getBounds() const { return iBound; }
static bool readFromFile(FILE *rf, ModelSpawn &spawn);
static bool writeToFile(FILE *rw, const ModelSpawn &spawn);
};
class ModelInstance: public ModelSpawn
{
public:
ModelInstance(): iModel(0) {}
ModelInstance(const ModelSpawn &spawn, WorldModel *model);
void setUnloaded() { iModel = 0; }
bool intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit) const;
void intersectPoint(const G3D::Vector3& p, AreaInfo &info) const;
bool GetLocationInfo(const G3D::Vector3& p, LocationInfo &info) const;
bool GetLiquidLevel(const G3D::Vector3& p, LocationInfo &info, float &liqHeight) const;
protected:
G3D::Matrix3 iInvRot;
float iInvScale;
WorldModel *iModel;
};
} // namespace VMAP
#endif // _MODELINSTANCE
@@ -1,494 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* 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.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "WorldModel.h"
#include "TileAssembler.h"
#include "MapTree.h"
#include "BIH.h"
#include "VMapDefinitions.h"
#include <set>
#include <iomanip>
#include <sstream>
#include <iomanip>
using G3D::Vector3;
using G3D::AABox;
using G3D::inf;
using std::pair;
template<> struct BoundsTrait<VMAP::ModelSpawn*>
{
static void getBounds(const VMAP::ModelSpawn* const &obj, G3D::AABox& out) { out = obj->getBounds(); }
};
namespace VMAP
{
bool readChunk(FILE *rf, char *dest, const char *compare, uint32 len)
{
if (fread(dest, sizeof(char), len, rf) != len) return false;
return memcmp(dest, compare, len) == 0;
}
Vector3 ModelPosition::transform(const Vector3& pIn) const
{
Vector3 out = pIn * iScale;
out = iRotation * out;
return(out);
}
//=================================================================
TileAssembler::TileAssembler(const std::string& pSrcDirName, const std::string& pDestDirName)
{
iCurrentUniqueNameId = 0;
iFilterMethod = NULL;
iSrcDir = pSrcDirName;
iDestDir = pDestDirName;
//mkdir(iDestDir);
//init();
}
TileAssembler::~TileAssembler()
{
//delete iCoordModelMapping;
}
bool TileAssembler::convertWorld2()
{
std::set<std::string> spawnedModelFiles;
bool success = readMapSpawns();
if (!success)
return false;
// export Map data
for (MapData::iterator map_iter = mapData.begin(); map_iter != mapData.end() && success; ++map_iter)
{
// build global map tree
std::vector<ModelSpawn*> mapSpawns;
UniqueEntryMap::iterator entry;
for (entry = map_iter->second->UniqueEntries.begin(); entry != map_iter->second->UniqueEntries.end(); ++entry)
{
// M2 models don't have a bound set in WDT/ADT placement data, i still think they're not used for LoS at all on retail
if (entry->second.flags & MOD_M2)
{
if (!calculateTransformedBound(entry->second))
break;
}
else if (entry->second.flags & MOD_WORLDSPAWN) // WMO maps and terrain maps use different origin, so we need to adapt :/
{
// TODO: remove extractor hack and uncomment below line:
//entry->second.iPos += Vector3(533.33333f*32, 533.33333f*32, 0.f);
entry->second.iBound = entry->second.iBound + Vector3(533.33333f*32, 533.33333f*32, 0.f);
}
mapSpawns.push_back(&(entry->second));
spawnedModelFiles.insert(entry->second.name);
}
BIH pTree;
pTree.build(mapSpawns, BoundsTrait<ModelSpawn*>::getBounds);
// ===> possibly move this code to StaticMapTree class
std::map<uint32, uint32> modelNodeIdx;
for (uint32 i=0; i<mapSpawns.size(); ++i)
modelNodeIdx.insert(pair<uint32, uint32>(mapSpawns[i]->ID, i));
if (!modelNodeIdx.empty())
printf("min GUID: %u, max GUID: %u\n", modelNodeIdx.begin()->first, modelNodeIdx.rbegin()->first);
// write map tree file
std::stringstream mapfilename;
mapfilename << iDestDir << "/" << std::setfill('0') << std::setw(3) << map_iter->first << ".vmtree";
FILE *mapfile = fopen(mapfilename.str().c_str(), "wb");
if (!mapfile)
{
success = false;
printf("Cannot open %s\n", mapfilename.str().c_str());
break;
}
//general info
if (success && fwrite(VMAP_MAGIC, 1, 8, mapfile) != 8) success = false;
uint32 globalTileID = StaticMapTree::packTileID(65, 65);
pair<TileMap::iterator, TileMap::iterator> globalRange = map_iter->second->TileEntries.equal_range(globalTileID);
char isTiled = globalRange.first == globalRange.second; // only maps without terrain (tiles) have global WMO
if (success && fwrite(&isTiled, sizeof(char), 1, mapfile) != 1) success = false;
// Nodes
if (success && fwrite("NODE", 4, 1, mapfile) != 1) success = false;
if (success) success = pTree.writeToFile(mapfile);
// global map spawns (WDT), if any (most instances)
if (success && fwrite("GOBJ", 4, 1, mapfile) != 1) success = false;
for (TileMap::iterator glob=globalRange.first; glob != globalRange.second && success; ++glob)
{
success = ModelSpawn::writeToFile(mapfile, map_iter->second->UniqueEntries[glob->second]);
}
fclose(mapfile);
// <====
// write map tile files, similar to ADT files, only with extra BSP tree node info
TileMap &tileEntries = map_iter->second->TileEntries;
TileMap::iterator tile;
for (tile = tileEntries.begin(); tile != tileEntries.end(); ++tile)
{
const ModelSpawn &spawn = map_iter->second->UniqueEntries[tile->second];
if (spawn.flags & MOD_WORLDSPAWN) // WDT spawn, saved as tile 65/65 currently...
continue;
uint32 nSpawns = tileEntries.count(tile->first);
std::stringstream tilefilename;
tilefilename.fill('0');
tilefilename << iDestDir << "/" << std::setw(3) << map_iter->first << "_";
uint32 x, y;
StaticMapTree::unpackTileID(tile->first, x, y);
tilefilename << std::setw(2) << x << "_" << std::setw(2) << y << ".vmtile";
FILE *tilefile = fopen(tilefilename.str().c_str(), "wb");
// write number of tile spawns
if (success && fwrite(&nSpawns, sizeof(uint32), 1, tilefile) != 1) success = false;
// write tile spawns
for (uint32 s=0; s<nSpawns; ++s)
{
if (s)
++tile;
const ModelSpawn &spawn2 = map_iter->second->UniqueEntries[tile->second];
success = success && ModelSpawn::writeToFile(tilefile, spawn2);
// MapTree nodes to update when loading tile:
std::map<uint32, uint32>::iterator nIdx = modelNodeIdx.find(spawn2.ID);
if (success && fwrite(&nIdx->second, sizeof(uint32), 1, tilefile) != 1) success = false;
}
fclose(tilefile);
}
// break; //test, extract only first map; TODO: remvoe this line
}
// export objects
std::cout << "\nConverting Model Files" << std::endl;
for (std::set<std::string>::iterator mfile = spawnedModelFiles.begin(); mfile != spawnedModelFiles.end(); ++mfile)
{
std::cout << "Converting " << *mfile << std::endl;
if (!convertRawFile(*mfile))
{
std::cout << "error converting " << *mfile << std::endl;
success = false;
break;
}
}
//cleanup:
for (MapData::iterator map_iter = mapData.begin(); map_iter != mapData.end(); ++map_iter)
{
delete map_iter->second;
}
return success;
}
bool TileAssembler::readMapSpawns()
{
std::string fname = iSrcDir + "/dir_bin";
FILE *dirf = fopen(fname.c_str(), "rb");
if (!dirf)
{
printf("Could not read dir_bin file!\n");
return false;
}
printf("Read coordinate mapping...\n");
uint32 mapID, tileX, tileY, check=0;
G3D::Vector3 v1, v2;
ModelSpawn spawn;
while (!feof(dirf))
{
check = 0;
// read mapID, tileX, tileY, Flags, adtID, ID, Pos, Rot, Scale, Bound_lo, Bound_hi, name
check += fread(&mapID, sizeof(uint32), 1, dirf);
if (check == 0) // EoF...
break;
check += fread(&tileX, sizeof(uint32), 1, dirf);
check += fread(&tileY, sizeof(uint32), 1, dirf);
if (!ModelSpawn::readFromFile(dirf, spawn))
break;
MapSpawns *current;
MapData::iterator map_iter = mapData.find(mapID);
if (map_iter == mapData.end())
{
printf("spawning Map %d\n", mapID);
mapData[mapID] = current = new MapSpawns();
}
else current = (*map_iter).second;
current->UniqueEntries.insert(pair<uint32, ModelSpawn>(spawn.ID, spawn));
current->TileEntries.insert(pair<uint32, uint32>(StaticMapTree::packTileID(tileX, tileY), spawn.ID));
}
bool success = (ferror(dirf) == 0);
fclose(dirf);
return success;
}
bool TileAssembler::calculateTransformedBound(ModelSpawn &spawn)
{
std::string modelFilename = iSrcDir + "/" + spawn.name;
ModelPosition modelPosition;
modelPosition.iDir = spawn.iRot;
modelPosition.iScale = spawn.iScale;
modelPosition.init();
FILE *rf = fopen(modelFilename.c_str(), "rb");
if (!rf)
{
printf("ERROR: Can't open model file: %s\n", modelFilename.c_str());
return false;
}
AABox modelBound;
bool boundEmpty=true;
char ident[8];
int readOperation = 1;
// temporary use defines to simplify read/check code (close file and return at fail)
#define READ_OR_RETURN(V,S) if(fread((V), (S), 1, rf) != 1) { \
fclose(rf); printf("readfail, op = %i\n", readOperation); return(false); }readOperation++;
#define CMP_OR_RETURN(V,S) if(strcmp((V),(S)) != 0) { \
fclose(rf); printf("cmpfail, %s!=%s\n", V, S);return(false); }
READ_OR_RETURN(&ident, 8);
CMP_OR_RETURN(ident, "VMAP003");
// we have to read one int. This is needed during the export and we have to skip it here
uint32 tempNVectors;
READ_OR_RETURN(&tempNVectors, sizeof(tempNVectors));
uint32 groups, wmoRootId;
char blockId[5];
blockId[4] = 0;
int blocksize;
float *vectorarray = 0;
READ_OR_RETURN(&groups, sizeof(uint32));
READ_OR_RETURN(&wmoRootId, sizeof(uint32));
if (groups != 1) printf("Warning: '%s' does not seem to be a M2 model!\n", modelFilename.c_str());
for (uint32 g=0; g<groups; ++g) // should be only one for M2 files...
{
fseek(rf, 3*sizeof(uint32) + 6*sizeof(float), SEEK_CUR);
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "GRP ");
READ_OR_RETURN(&blocksize, sizeof(int));
fseek(rf, blocksize, SEEK_CUR);
// ---- indexes
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "INDX");
READ_OR_RETURN(&blocksize, sizeof(int));
fseek(rf, blocksize, SEEK_CUR);
// ---- vectors
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "VERT");
READ_OR_RETURN(&blocksize, sizeof(int));
uint32 nvectors;
READ_OR_RETURN(&nvectors, sizeof(uint32));
if (nvectors >0)
{
vectorarray = new float[nvectors*3];
READ_OR_RETURN(vectorarray, nvectors*sizeof(float)*3);
}
else
{
std::cout << "error: model '" << spawn.name << "' has no geometry!" << std::endl;
return false;
}
for (uint32 i=0, indexNo=0; indexNo<nvectors; indexNo++, i+=3)
{
Vector3 v = Vector3(vectorarray[i+0], vectorarray[i+1], vectorarray[i+2]);
v = modelPosition.transform(v);
if (boundEmpty)
modelBound = AABox(v, v), boundEmpty=false;
else
modelBound.merge(v);
}
delete[] vectorarray;
// drop of temporary use defines
#undef READ_OR_RETURN
#undef CMP_OR_RETURN
}
spawn.iBound = modelBound + spawn.iPos;
spawn.flags |= MOD_HAS_BOUND;
fclose(rf);
return true;
}
struct WMOLiquidHeader
{
int xverts, yverts, xtiles, ytiles;
float pos_x;
float pos_y;
float pos_z;
short type;
};
//=================================================================
bool TileAssembler::convertRawFile(const std::string& pModelFilename)
{
bool success = true;
std::string filename = iSrcDir;
if (filename.length() >0)
filename.append("/");
filename.append(pModelFilename);
FILE *rf = fopen(filename.c_str(), "rb");
if (!rf)
{
printf("ERROR: Can't open model file in form: %s",pModelFilename.c_str());
printf("... or form: %s",filename.c_str() );
return false;
}
char ident[8];
int readOperation = 1;
// temporary use defines to simplify read/check code (close file and return at fail)
#define READ_OR_RETURN(V,S) if(fread((V), (S), 1, rf) != 1) { \
fclose(rf); printf("readfail, op = %i\n", readOperation); return(false); }readOperation++;
#define CMP_OR_RETURN(V,S) if(strcmp((V),(S)) != 0) { \
fclose(rf); printf("cmpfail, %s!=%s\n", V, S);return(false); }
READ_OR_RETURN(&ident, 8);
CMP_OR_RETURN(ident, "VMAP003");
// we have to read one int. This is needed during the export and we have to skip it here
uint32 tempNVectors;
READ_OR_RETURN(&tempNVectors, sizeof(tempNVectors));
uint32 groups;
uint32 RootWMOID;
char blockId[5];
blockId[4] = 0;
int blocksize;
READ_OR_RETURN(&groups, sizeof(uint32));
READ_OR_RETURN(&RootWMOID, sizeof(uint32));
std::vector<GroupModel> groupsArray;
for (uint32 g=0; g<groups; ++g)
{
std::vector<MeshTriangle> triangles;
std::vector<Vector3> vertexArray;
uint32 mogpflags, GroupWMOID;
READ_OR_RETURN(&mogpflags, sizeof(uint32));
READ_OR_RETURN(&GroupWMOID, sizeof(uint32));
float bbox1[3], bbox2[3];
READ_OR_RETURN(bbox1, sizeof(float)*3);
READ_OR_RETURN(bbox2, sizeof(float)*3);
uint32 liquidflags;
READ_OR_RETURN(&liquidflags, sizeof(uint32));
// will this ever be used? what is it good for anyway??
uint32 branches;
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "GRP ");
READ_OR_RETURN(&blocksize, sizeof(int));
READ_OR_RETURN(&branches, sizeof(uint32));
for (uint32 b=0; b<branches; ++b)
{
uint32 indexes;
// indexes for each branch (not used jet)
READ_OR_RETURN(&indexes, sizeof(uint32));
}
// ---- indexes
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "INDX");
READ_OR_RETURN(&blocksize, sizeof(int));
uint32 nindexes;
READ_OR_RETURN(&nindexes, sizeof(uint32));
if (nindexes >0)
{
uint16 *indexarray = new uint16[nindexes];
READ_OR_RETURN(indexarray, nindexes*sizeof(uint16));
for (uint32 i=0; i<nindexes; i+=3)
{
triangles.push_back(MeshTriangle(indexarray[i], indexarray[i+1], indexarray[i+2]));
}
delete[] indexarray;
}
// ---- vectors
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "VERT");
READ_OR_RETURN(&blocksize, sizeof(int));
uint32 nvectors;
READ_OR_RETURN(&nvectors, sizeof(uint32));
if (nvectors >0)
{
float *vectorarray = new float[nvectors*3];
READ_OR_RETURN(vectorarray, nvectors*sizeof(float)*3);
for (uint32 i=0; i<nvectors; ++i)
{
vertexArray.push_back( Vector3(vectorarray + 3*i) );
}
delete[] vectorarray;
}
// ----- liquid
WmoLiquid *liquid = 0;
if (liquidflags& 1)
{
WMOLiquidHeader hlq;
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "LIQU");
READ_OR_RETURN(&blocksize, sizeof(int));
READ_OR_RETURN(&hlq, sizeof(WMOLiquidHeader));
liquid = new WmoLiquid(hlq.xtiles, hlq.ytiles, Vector3(hlq.pos_x, hlq.pos_y, hlq.pos_z), hlq.type);
uint32 size = hlq.xverts*hlq.yverts;
READ_OR_RETURN(liquid->GetHeightStorage(), size*sizeof(float));
size = hlq.xtiles*hlq.ytiles;
READ_OR_RETURN(liquid->GetFlagsStorage(), size);
}
groupsArray.push_back(GroupModel(mogpflags, GroupWMOID, AABox(Vector3(bbox1), Vector3(bbox2))));
groupsArray.back().setMeshData(vertexArray, triangles);
groupsArray.back().setLiquidData(liquid);
// drop of temporary use defines
#undef READ_OR_RETURN
#undef CMP_OR_RETURN
}
fclose(rf);
// write WorldModel
WorldModel model;
model.setRootWmoID(RootWMOID);
if (groupsArray.size())
{
model.setGroupModels(groupsArray);
success = model.writeFile(iDestDir + "/" + pModelFilename + ".vmo");
}
//std::cout << "readRawFile2: '" << pModelFilename << "' tris: " << nElements << " nodes: " << nNodes << std::endl;
return success;
}
}
@@ -1,89 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* 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.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _TILEASSEMBLER_H_
#define _TILEASSEMBLER_H_
#include <G3D/Vector3.h>
#include <G3D/Matrix3.h>
#include <map>
#include "ModelInstance.h"
namespace VMAP
{
/**
This Class is used to convert raw vector data into balanced BSP-Trees.
To start the conversion call convertWorld().
*/
//===============================================
class ModelPosition
{
private:
G3D::Matrix3 iRotation;
public:
G3D::Vector3 iPos;
G3D::Vector3 iDir;
float iScale;
void init()
{
iRotation = G3D::Matrix3::fromEulerAnglesZYX(G3D::pi()*iDir.y/180.f, G3D::pi()*iDir.x/180.f, G3D::pi()*iDir.z/180.f);
}
G3D::Vector3 transform(const G3D::Vector3& pIn) const;
void moveToBasePos(const G3D::Vector3& pBasePos) { iPos -= pBasePos; }
};
typedef std::map<uint32, ModelSpawn> UniqueEntryMap;
typedef std::multimap<uint32, uint32> TileMap;
struct MapSpawns
{
UniqueEntryMap UniqueEntries;
TileMap TileEntries;
};
typedef std::map<uint32, MapSpawns*> MapData;
//===============================================
class TileAssembler
{
private:
std::string iDestDir;
std::string iSrcDir;
bool (*iFilterMethod)(char *pName);
G3D::Table<std::string, unsigned int > iUniqueNameIds;
unsigned int iCurrentUniqueNameId;
MapData mapData;
public:
TileAssembler(const std::string& pSrcDirName, const std::string& pDestDirName);
virtual ~TileAssembler();
bool convertWorld2();
bool readMapSpawns();
bool calculateTransformedBound(ModelSpawn &spawn);
bool convertRawFile(const std::string& pModelFilename);
void setModelNameFilterMethod(bool (*pFilterMethod)(char *pName)) { iFilterMethod = pFilterMethod; }
std::string getDirEntryNameFromModName(unsigned int pMapId, const std::string& pModPosName);
unsigned int getUniqueNameId(const std::string pName);
};
} // VMAP
#endif /*_TILEASSEMBLER_H_*/
@@ -1,42 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* 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.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _VMAPDEFINITIONS_H
#define _VMAPDEFINITIONS_H
#include <cstring>
#define LIQUID_TILE_SIZE (533.333f / 128.f)
namespace VMAP
{
//=====================================
#define MAX_CAN_FALL_DISTANCE 10.0f
const char VMAP_MAGIC[] = "VMAP_3.0";
class VMapDefinitions
{
public:
static float getMaxCanFallDistance() { return MAX_CAN_FALL_DISTANCE; }
};
//======================================
// defined in TileAssembler.cpp currently...
bool readChunk(FILE *rf, char *dest, const char *compare, uint32 len);
}
#endif
@@ -1,136 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* 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.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sys/types.h>
#include "VMapFactory.h"
#include "VMapManager2.h"
using namespace G3D;
namespace VMAP
{
void chompAndTrim(std::string& str)
{
while(str.length() >0)
{
char lc = str[str.length()-1];
if(lc == '\r' || lc == '\n' || lc == ' ' || lc == '"' || lc == '\'')
{
str = str.substr(0,str.length()-1);
}
else
{
break;
}
}
while(str.length() >0)
{
char lc = str[0];
if(lc == ' ' || lc == '"' || lc == '\'')
{
str = str.substr(1,str.length()-1);
}
else
{
break;
}
}
}
IVMapManager *gVMapManager = 0;
Table<unsigned int , bool>* iIgnoreSpellIds=0;
//===============================================
// result false, if no more id are found
bool getNextId(const std::string& pString, unsigned int& pStartPos, unsigned int& pId)
{
bool result = false;
unsigned int i;
for(i=pStartPos;i<pString.size(); ++i)
{
if(pString[i] == ',')
{
break;
}
}
if(i>pStartPos)
{
std::string idString = pString.substr(pStartPos, i-pStartPos);
pStartPos = i+1;
chompAndTrim(idString);
pId = atoi(idString.c_str());
result = true;
}
return(result);
}
//===============================================
/**
parameter: String of map ids. Delimiter = ","
*/
void VMapFactory::preventSpellsFromBeingTestedForLoS(const char* pSpellIdString)
{
if(!iIgnoreSpellIds)
iIgnoreSpellIds = new Table<unsigned int , bool>();
if(pSpellIdString != NULL)
{
unsigned int pos =0;
unsigned int id;
std::string confString(pSpellIdString);
chompAndTrim(confString);
while(getNextId(confString, pos, id))
{
iIgnoreSpellIds->set(id, true);
}
}
}
//===============================================
bool VMapFactory::checkSpellForLoS(unsigned int pSpellId)
{
return(!iIgnoreSpellIds->containsKey(pSpellId));
}
//===============================================
// just return the instance
IVMapManager* VMapFactory::createOrGetVMapManager()
{
if(gVMapManager == 0)
gVMapManager= new VMapManager2(); // should be taken from config ... Please change if you like :-)
return gVMapManager;
}
//===============================================
// delete all internal data structures
void VMapFactory::clear()
{
if(iIgnoreSpellIds)
{
delete iIgnoreSpellIds;
iIgnoreSpellIds = NULL;
}
if(gVMapManager)
{
delete gVMapManager;
gVMapManager = NULL;
}
}
}
@@ -1,43 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* 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.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _VMAPFACTORY_H
#define _VMAPFACTORY_H
#include "IVMapManager.h"
/**
This is the access point to the VMapManager.
*/
namespace VMAP
{
//===========================================================
class VMapFactory
{
public:
static IVMapManager* createOrGetVMapManager();
static void clear();
static void preventSpellsFromBeingTestedForLoS(const char* pSpellIdString);
static bool checkSpellForLoS(unsigned int pSpellId);
};
}
#endif
@@ -1,336 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* 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.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include "VMapManager2.h"
#include "MapTree.h"
#include "ModelInstance.h"
#include "WorldModel.h"
#include "VMapDefinitions.h"
using G3D::Vector3;
namespace VMAP
{
//=========================================================
VMapManager2::VMapManager2()
{
}
//=========================================================
VMapManager2::~VMapManager2(void)
{
for (InstanceTreeMap::iterator i = iInstanceMapTrees.begin(); i != iInstanceMapTrees.end(); ++i)
{
delete i->second;
}
for (ModelFileMap::iterator i = iLoadedModelFiles.begin(); i != iLoadedModelFiles.end(); ++i)
{
delete i->second.getModel();
}
}
//=========================================================
Vector3 VMapManager2::convertPositionToInternalRep(float x, float y, float z) const
{
Vector3 pos;
const float mid = 0.5 * 64.0 * 533.33333333f;
pos.x = mid - x;
pos.y = mid - y;
pos.z = z;
return pos;
}
//=========================================================
Vector3 VMapManager2::convertPositionToMangosRep(float x, float y, float z) const
{
Vector3 pos;
const float mid = 0.5 * 64.0 * 533.33333333f;
pos.x = mid - x;
pos.y = mid - y;
pos.z = z;
return pos;
}
//=========================================================
// move to MapTree too?
std::string VMapManager2::getMapFileName(unsigned int pMapId)
{
std::stringstream fname;
fname.width(3);
fname << std::setfill('0') << pMapId << std::string(MAP_FILENAME_EXTENSION2);
return fname.str();
}
//=========================================================
/**
Block maps from being used.
parameter: String of map ids. Delimiter = ","
e.g.: "0,1,590"
*/
void VMapManager2::preventMapsFromBeingUsed(const char* pMapIdString)
{
iIgnoreMapIds.clear();
if (pMapIdString != NULL)
{
std::string map_str;
std::stringstream map_ss;
map_ss.str(std::string(pMapIdString));
while (std::getline(map_ss, map_str, ','))
{
std::stringstream ss2(map_str);
int map_num = -1;
ss2 >> map_num;
if (map_num >= 0)
{
std::cout << "ingoring Map " << map_num << " for VMaps\n";
iIgnoreMapIds[map_num] = true;
// unload map in case it is loaded
unloadMap(map_num);
}
}
}
}
//=========================================================
int VMapManager2::loadMap(const char* pBasePath, unsigned int pMapId, int x, int y)
{
int result = VMAP_LOAD_RESULT_IGNORED;
if (isMapLoadingEnabled() && !iIgnoreMapIds.count(pMapId))
{
if (_loadMap(pMapId, pBasePath, x, y))
result = VMAP_LOAD_RESULT_OK;
else
result = VMAP_LOAD_RESULT_ERROR;
}
return result;
}
//=========================================================
// load one tile (internal use only)
bool VMapManager2::_loadMap(unsigned int pMapId, const std::string &basePath, uint32 tileX, uint32 tileY)
{
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(pMapId);
if (instanceTree == iInstanceMapTrees.end())
{
std::string mapFileName = getMapFileName(pMapId);
StaticMapTree *newTree = new StaticMapTree(pMapId, basePath);
if (!newTree->InitMap(mapFileName, this))
return false;
instanceTree = iInstanceMapTrees.insert(InstanceTreeMap::value_type(pMapId, newTree)).first;
}
return instanceTree->second->LoadMapTile(tileX, tileY, this);
}
//=========================================================
void VMapManager2::unloadMap(unsigned int pMapId)
{
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(pMapId);
if (instanceTree != iInstanceMapTrees.end())
{
instanceTree->second->UnloadMap(this);
if (instanceTree->second->numLoadedTiles() == 0)
{
delete instanceTree->second;
iInstanceMapTrees.erase(pMapId);
}
}
}
//=========================================================
void VMapManager2::unloadMap(unsigned int pMapId, int x, int y)
{
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(pMapId);
if (instanceTree != iInstanceMapTrees.end())
{
instanceTree->second->UnloadMapTile(x, y, this);
if (instanceTree->second->numLoadedTiles() == 0)
{
delete instanceTree->second;
iInstanceMapTrees.erase(pMapId);
}
}
}
//==========================================================
bool VMapManager2::isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2)
{
if (!isLineOfSightCalcEnabled()) return true;
bool result = true;
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(pMapId);
if (instanceTree != iInstanceMapTrees.end())
{
Vector3 pos1 = convertPositionToInternalRep(x1,y1,z1);
Vector3 pos2 = convertPositionToInternalRep(x2,y2,z2);
if (pos1 != pos2)
{
result = instanceTree->second->isInLineOfSight(pos1, pos2);
}
}
return result;
}
//=========================================================
/**
get the hit position and return true if we hit something
otherwise the result pos will be the dest pos
*/
bool VMapManager2::getObjectHitPos(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float pModifyDist)
{
bool result = false;
rx=x2;
ry=y2;
rz=z2;
if (isLineOfSightCalcEnabled())
{
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(pMapId);
if (instanceTree != iInstanceMapTrees.end())
{
Vector3 pos1 = convertPositionToInternalRep(x1,y1,z1);
Vector3 pos2 = convertPositionToInternalRep(x2,y2,z2);
Vector3 resultPos;
result = instanceTree->second->getObjectHitPos(pos1, pos2, resultPos, pModifyDist);
resultPos = convertPositionToMangosRep(resultPos.x,resultPos.y,resultPos.z);
rx = resultPos.x;
ry = resultPos.y;
rz = resultPos.z;
}
}
return result;
}
//=========================================================
/**
get height or INVALID_HEIGHT if no height available
*/
float VMapManager2::getHeight(unsigned int pMapId, float x, float y, float z)
{
float height = VMAP_INVALID_HEIGHT_VALUE; //no height
if (isHeightCalcEnabled())
{
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(pMapId);
if (instanceTree != iInstanceMapTrees.end())
{
Vector3 pos = convertPositionToInternalRep(x,y,z);
height = instanceTree->second->getHeight(pos);
if (!(height < G3D::inf()))
{
height = VMAP_INVALID_HEIGHT_VALUE; //no height
}
}
}
return height;
}
//=========================================================
bool VMapManager2::getAreaInfo(unsigned int pMapId, float x, float y, float &z, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const
{
bool result=false;
InstanceTreeMap::const_iterator instanceTree = iInstanceMapTrees.find(pMapId);
if (instanceTree != iInstanceMapTrees.end())
{
Vector3 pos = convertPositionToInternalRep(x, y, z);
result = instanceTree->second->getAreaInfo(pos, flags, adtId, rootId, groupId);
// z is not touched by convertPositionToMangosRep(), so just copy
z = pos.z;
}
return(result);
}
bool VMapManager2::GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 ReqLiquidType, float &level, float &floor, uint32 &type) const
{
InstanceTreeMap::const_iterator instanceTree = iInstanceMapTrees.find(pMapId);
if (instanceTree != iInstanceMapTrees.end())
{
LocationInfo info;
Vector3 pos = convertPositionToInternalRep(x, y, z);
if (instanceTree->second->GetLocationInfo(pos, info))
{
floor = info.ground_Z;
type = info.hitModel->GetLiquidType();
if (ReqLiquidType && !(type & ReqLiquidType))
return false;
if (info.hitInstance->GetLiquidLevel(pos, info, level))
return true;
}
}
return false;
}
//=========================================================
WorldModel* VMapManager2::acquireModelInstance(const std::string &basepath, const std::string &filename)
{
ModelFileMap::iterator model = iLoadedModelFiles.find(filename);
if (model == iLoadedModelFiles.end())
{
WorldModel *worldmodel = new WorldModel();
if (!worldmodel->readFile(basepath + filename + ".vmo"))
{
std::cout << "VMapManager2: could not load '" << basepath << filename << ".vmo'!\n";
delete worldmodel;
return NULL;
}
std::cout << "VMapManager2: loading file '" << basepath << filename << "'.\n";
model = iLoadedModelFiles.insert(std::pair<std::string, ManagedModel>(filename, ManagedModel())).first;
model->second.setModel(worldmodel);
}
model->second.incRefCount();
return model->second.getModel();
}
void VMapManager2::releaseModelInstance(const std::string &filename)
{
ModelFileMap::iterator model = iLoadedModelFiles.find(filename);
if (model == iLoadedModelFiles.end())
{
std::cout << "VMapManager2: trying to unload non-loaded file '" << filename << "'!\n";
return;
}
if( model->second.decRefCount() == 0)
{
std::cout << "VMapManager2: unloading file '" << filename << "'.\n";
delete model->second.getModel();
iLoadedModelFiles.erase(model);
}
}
//=========================================================
bool VMapManager2::existsMap(const char* pBasePath, unsigned int pMapId, int x, int y)
{
return StaticMapTree::CanLoadMap(std::string(pBasePath), pMapId, x, y);
}
} // namespace VMAP
@@ -1,114 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* 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.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _VMAPMANAGER2_H
#define _VMAPMANAGER2_H
#include "IVMapManager.h"
#include "Utilities/UnorderedMap.h"
#include "Platform/Define.h"
#include <G3D/Vector3.h>
//===========================================================
#define MAP_FILENAME_EXTENSION2 ".vmtree"
#define FILENAMEBUFFER_SIZE 500
/**
This is the main Class to manage loading and unloading of maps, line of sight, height calculation and so on.
For each map or map tile to load it reads a directory file that contains the ModelContainer files used by this map or map tile.
Each global map or instance has its own dynamic BSP-Tree.
The loaded ModelContainers are included in one of these BSP-Trees.
Additionally a table to match map ids and map names is used.
*/
//===========================================================
namespace VMAP
{
class StaticMapTree;
class WorldModel;
class ManagedModel
{
public:
ManagedModel(): iModel(0), iRefCount(0) {}
void setModel(WorldModel *model) { iModel = model; }
WorldModel *getModel() { return iModel; }
void incRefCount() { ++iRefCount; }
int decRefCount() { return --iRefCount; }
protected:
WorldModel *iModel;
int iRefCount;
};
typedef UNORDERED_MAP<uint32 , StaticMapTree *> InstanceTreeMap;
typedef UNORDERED_MAP<std::string, ManagedModel> ModelFileMap;
class VMapManager2 : public IVMapManager
{
protected:
// Tree to check collision
ModelFileMap iLoadedModelFiles;
InstanceTreeMap iInstanceMapTrees;
// UNORDERED_MAP<unsigned int , bool> iMapsSplitIntoTiles;
UNORDERED_MAP<unsigned int , bool> iIgnoreMapIds;
bool _loadMap(uint32 pMapId, const std::string &basePath, uint32 tileX, uint32 tileY);
/* void _unloadMap(uint32 pMapId, uint32 x, uint32 y); */
public:
// public for debug
G3D::Vector3 convertPositionToInternalRep(float x, float y, float z) const;
G3D::Vector3 convertPositionToMangosRep(float x, float y, float z) const;
static std::string getMapFileName(unsigned int pMapId);
VMapManager2();
~VMapManager2(void);
int loadMap(const char* pBasePath, unsigned int pMapId, int x, int y);
void unloadMap(unsigned int pMapId, int x, int y);
void unloadMap(unsigned int pMapId);
bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2) ;
/**
fill the hit pos and return true, if an object was hit
*/
bool getObjectHitPos(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float pModifyDist);
float getHeight(unsigned int pMapId, float x, float y, float z);
bool processCommand(char *pCommand) { return false; } // for debug and extensions
void preventMapsFromBeingUsed(const char* pMapIdString);
bool getAreaInfo(unsigned int pMapId, float x, float y, float &z, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const;
bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 ReqLiquidType, float &level, float &floor, uint32 &type) const;
WorldModel* acquireModelInstance(const std::string &basepath, const std::string &filename);
void releaseModelInstance(const std::string &filename);
// what's the use of this? o.O
virtual std::string getDirFileName(unsigned int pMapId, int x, int y) const
{
return getMapFileName(pMapId);
}
virtual bool existsMap(const char* pBasePath, unsigned int pMapId, int x, int y);
};
}
#endif
@@ -1,150 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* 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.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _VMAPTOOLS_H
#define _VMAPTOOLS_H
#include <G3D/CollisionDetection.h>
#include <G3D/AABox.h>
#include "NodeValueAccess.h"
/**
The Class is mainly taken from G3D/AABSPTree.h but modified to be able to use our internal data structure.
This is an iterator that helps us analysing the BSP-Trees.
The collision detection is modified to return true, if we are inside an object.
*/
namespace VMAP
{
template<class TValue>
class IntersectionCallBack {
public:
TValue* closestEntity;
G3D::Vector3 hitLocation;
G3D::Vector3 hitNormal;
void operator()(const G3D::Ray& ray, const TValue* entity, bool pStopAtFirstHit, float& distance) {
entity->intersect(ray, distance, pStopAtFirstHit, hitLocation, hitNormal);
}
};
//==============================================================
//==============================================================
//==============================================================
class MyCollisionDetection
{
private:
public:
static bool collisionLocationForMovingPointFixedAABox(
const G3D::Vector3& origin,
const G3D::Vector3& dir,
const G3D::AABox& box,
G3D::Vector3& location,
bool& Inside)
{
// Integer representation of a floating-point value.
#define IR(x) (reinterpret_cast<G3D::uint32 const&>(x))
Inside = true;
const G3D::Vector3& MinB = box.low();
const G3D::Vector3& MaxB = box.high();
G3D::Vector3 MaxT(-1.0f, -1.0f, -1.0f);
// Find candidate planes.
for (int i = 0; i < 3; ++i)
{
if (origin[i] < MinB[i])
{
location[i] = MinB[i];
Inside = false;
// Calculate T distances to candidate planes
if (IR(dir[i]))
{
MaxT[i] = (MinB[i] - origin[i]) / dir[i];
}
}
else if (origin[i] > MaxB[i])
{
location[i] = MaxB[i];
Inside = false;
// Calculate T distances to candidate planes
if (IR(dir[i]))
{
MaxT[i] = (MaxB[i] - origin[i]) / dir[i];
}
}
}
if (Inside)
{
// definite hit
location = origin;
return true;
}
// Get largest of the maxT's for final choice of intersection
int WhichPlane = 0;
if (MaxT[1] > MaxT[WhichPlane])
{
WhichPlane = 1;
}
if (MaxT[2] > MaxT[WhichPlane])
{
WhichPlane = 2;
}
// Check final candidate actually inside box
if (IR(MaxT[WhichPlane]) & 0x80000000)
{
// Miss the box
return false;
}
for (int i = 0; i < 3; ++i)
{
if (i != WhichPlane)
{
location[i] = origin[i] + MaxT[WhichPlane] * dir[i];
if ((location[i] < MinB[i]) ||
(location[i] > MaxB[i]))
{
// On this plane we're outside the box extents, so
// we miss the box
return false;
}
}
}
/*
// Choose the normal to be the plane normal facing into the ray
normal = G3D::Vector3::zero();
normal[WhichPlane] = (dir[WhichPlane] > 0) ? -1.0 : 1.0;
*/
return true;
#undef IR
}
};
}
#endif
@@ -1,535 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* 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.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "WorldModel.h"
#include "VMapDefinitions.h"
#include "MapTree.h"
using G3D::Vector3;
using G3D::Ray;
template<> struct BoundsTrait<VMAP::GroupModel>
{
static void getBounds(const VMAP::GroupModel& obj, G3D::AABox& out) { out = obj.GetBound(); }
};
namespace VMAP
{
bool IntersectTriangle(const MeshTriangle &tri, std::vector<Vector3>::const_iterator points, const G3D::Ray &ray, float &distance)
{
static const float EPS = 1e-5f;
// See RTR2 ch. 13.7 for the algorithm.
const Vector3 e1 = points[tri.idx1] - points[tri.idx0];
const Vector3 e2 = points[tri.idx2] - points[tri.idx0];
const Vector3 p(ray.direction().cross(e2));
const float a = e1.dot(p);
if (abs(a) < EPS) {
// Determinant is ill-conditioned; abort early
return false;
}
const float f = 1.0f / a;
const Vector3 s(ray.origin() - points[tri.idx0]);
const float u = f * s.dot(p);
if ((u < 0.0f) || (u > 1.0f)) {
// We hit the plane of the m_geometry, but outside the m_geometry
return false;
}
const Vector3 q(s.cross(e1));
const float v = f * ray.direction().dot(q);
if ((v < 0.0f) || ((u + v) > 1.0f)) {
// We hit the plane of the triangle, but outside the triangle
return false;
}
const float t = f * e2.dot(q);
if ((t > 0.0f) && (t < distance))
{
// This is a new hit, closer than the previous one
distance = t;
/* baryCoord[0] = 1.0 - u - v;
baryCoord[1] = u;
baryCoord[2] = v; */
return true;
}
// This hit is after the previous hit, so ignore it
return false;
}
class TriBoundFunc
{
public:
TriBoundFunc(std::vector<Vector3> &vert): vertices(vert.begin()) {}
void operator()(const MeshTriangle &tri, G3D::AABox &out) const
{
G3D::Vector3 lo = vertices[tri.idx0];
G3D::Vector3 hi = lo;
lo = (lo.min(vertices[tri.idx1])).min(vertices[tri.idx2]);
hi = (hi.max(vertices[tri.idx1])).max(vertices[tri.idx2]);
out = G3D::AABox(lo, hi);
}
protected:
const std::vector<Vector3>::const_iterator vertices;
};
// ===================== WmoLiquid ==================================
WmoLiquid::WmoLiquid(uint32 width, uint32 height, const Vector3 &corner, uint32 type):
iTilesX(width), iTilesY(height), iCorner(corner), iType(type)
{
iHeight = new float[(width+1)*(height+1)];
iFlags = new uint8[width*height];
}
WmoLiquid::WmoLiquid(const WmoLiquid &other): iHeight(0), iFlags(0)
{
*this = other; // use assignment operator...
}
WmoLiquid::~WmoLiquid()
{
delete[] iHeight;
delete[] iFlags;
}
WmoLiquid& WmoLiquid::operator=(const WmoLiquid &other)
{
if (this == &other)
return *this;
iTilesX = other.iTilesX;
iTilesY = other.iTilesY;
iCorner = other.iCorner;
iType = other.iType;
delete iHeight;
delete iFlags;
if (other.iHeight)
{
iHeight = new float[(iTilesX+1)*(iTilesY+1)];
memcpy(iHeight, other.iHeight, (iTilesX+1)*(iTilesY+1)*sizeof(float));
}
else
iHeight = 0;
if (other.iFlags)
{
iFlags = new uint8[iTilesX * iTilesY];
memcpy(iFlags, other.iFlags, iTilesX * iTilesY);
}
else
iFlags = 0;
return *this;
}
bool WmoLiquid::GetLiquidHeight(const Vector3 &pos, float &liqHeight) const
{
uint32 tx = (pos.x - iCorner.x)/LIQUID_TILE_SIZE;
if (tx<0 || tx >= iTilesX) return false;
uint32 ty = (pos.y - iCorner.y)/LIQUID_TILE_SIZE;
if (ty<0 || ty >= iTilesY) return false;
// checking for 0x08 *might* be enough, but disabled tiles always are 0x?F:
if ((iFlags[tx + ty*iTilesX] & 0x0F) == 0x0F)
return false;
//placeholder...use only lower left corner vertex
liqHeight = /* iCorner.z + */ iHeight[tx + ty*(iTilesX+1)];
return true;
}
uint32 WmoLiquid::GetFileSize()
{
return 2 * sizeof(uint32) +
sizeof(Vector3) +
(iTilesX + 1)*(iTilesY + 1) * sizeof(float) +
iTilesX * iTilesY;
}
bool WmoLiquid::writeToFile(FILE *wf)
{
bool result = true;
if (result && fwrite(&iTilesX, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&iTilesY, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&iCorner, sizeof(Vector3), 1, wf) != 1) result = false;
if (result && fwrite(&iType, sizeof(uint32), 1, wf) != 1) result = false;
uint32 size = (iTilesX + 1)*(iTilesY + 1);
if (result && fwrite(iHeight, sizeof(float), size, wf) != size) result = false;
size = iTilesX*iTilesY;
if (result && fwrite(iFlags, sizeof(uint8), size, wf) != size) result = false;
return result;
}
bool WmoLiquid::readFromFile(FILE *rf, WmoLiquid *&out)
{
bool result = true;
WmoLiquid *liquid = new WmoLiquid();
if (result && fread(&liquid->iTilesX, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&liquid->iTilesY, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&liquid->iCorner, sizeof(Vector3), 1, rf) != 1) result = false;
if (result && fread(&liquid->iType, sizeof(uint32), 1, rf) != 1) result = false;
uint32 size = (liquid->iTilesX + 1)*(liquid->iTilesY + 1);
liquid->iHeight = new float[size];
if (result && fread(liquid->iHeight, sizeof(float), size, rf) != size) result = false;
size = liquid->iTilesX * liquid->iTilesY;
liquid->iFlags = new uint8[size];
if (result && fread(liquid->iFlags, sizeof(uint8), size, rf) != size) result = false;
if (!result)
delete liquid;
out = liquid;
return result;
}
// ===================== GroupModel ==================================
GroupModel::GroupModel(const GroupModel &other):
iBound(other.iBound), iMogpFlags(other.iMogpFlags), iGroupWMOID(other.iGroupWMOID),
vertices(other.vertices), triangles(other.triangles), meshTree(other.meshTree), iLiquid(0)
{
if (other.iLiquid)
iLiquid = new WmoLiquid(*other.iLiquid);
}
void GroupModel::setMeshData(std::vector<Vector3> &vert, std::vector<MeshTriangle> &tri)
{
vertices.swap(vert);
triangles.swap(tri);
TriBoundFunc bFunc(vertices);
meshTree.build(triangles, bFunc);
}
bool GroupModel::writeToFile(FILE *wf)
{
bool result = true;
uint32 chunkSize, count;
if (result && fwrite(&iBound, sizeof(G3D::AABox), 1, wf) != 1) result = false;
if (result && fwrite(&iMogpFlags, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&iGroupWMOID, sizeof(uint32), 1, wf) != 1) result = false;
// write vertices
if (result && fwrite("VERT", 1, 4, wf) != 4) result = false;
count = vertices.size();
chunkSize = sizeof(uint32)+ sizeof(Vector3)*count;
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) result = false;
if (!count) // models without (collision) geometry end here, unsure if they are useful
return result;
if (result && fwrite(&vertices[0], sizeof(Vector3), count, wf) != count) result = false;
// write triangle mesh
if (result && fwrite("TRIM", 1, 4, wf) != 4) result = false;
count = triangles.size();
chunkSize = sizeof(uint32)+ sizeof(MeshTriangle)*count;
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&triangles[0], sizeof(MeshTriangle), count, wf) != count) result = false;
// write mesh BIH
if (result && fwrite("MBIH", 1, 4, wf) != 4) result = false;
if (result) result = meshTree.writeToFile(wf);
// write liquid data
if (result && fwrite("LIQU", 1, 4, wf) != 4) result = false;
if (!iLiquid)
{
chunkSize = 0;
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
return result;
}
chunkSize = iLiquid->GetFileSize();
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
if (result) result = iLiquid->writeToFile(wf);
return result;
}
bool GroupModel::readFromFile(FILE *rf)
{
char chunk[8];
bool result = true;
uint32 chunkSize, count;
triangles.clear();
vertices.clear();
delete iLiquid;
iLiquid = 0;
if (result && fread(&iBound, sizeof(G3D::AABox), 1, rf) != 1) result = false;
if (result && fread(&iMogpFlags, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&iGroupWMOID, sizeof(uint32), 1, rf) != 1) result = false;
// read vertices
if (result && !readChunk(rf, chunk, "VERT", 4)) result = false;
if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&count, sizeof(uint32), 1, rf) != 1) result = false;
if (!count) // models without (collision) geometry end here, unsure if they are useful
return result;
if (result) vertices.resize(count);
if (result && fread(&vertices[0], sizeof(Vector3), count, rf) != count) result = false;
// read triangle mesh
if (result && !readChunk(rf, chunk, "TRIM", 4)) result = false;
if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&count, sizeof(uint32), 1, rf) != 1) result = false;
if (result) triangles.resize(count);
if (result && fread(&triangles[0], sizeof(MeshTriangle), count, rf) != count) result = false;
// read mesh BIH
if (result && !readChunk(rf, chunk, "MBIH", 4)) result = false;
if (result) result = meshTree.readFromFile(rf);
// write liquid data
if (result && !readChunk(rf, chunk, "LIQU", 4)) result = false;
if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
if (result && chunkSize > 0)
result = WmoLiquid::readFromFile(rf, iLiquid);
return result;
}
struct GModelRayCallback
{
GModelRayCallback(const std::vector<MeshTriangle> &tris, const std::vector<Vector3> &vert):
vertices(vert.begin()), triangles(tris.begin()), hit(false) {}
bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool pStopAtFirstHit)
{
bool result = IntersectTriangle(triangles[entry], vertices, ray, distance);
if (result) hit=true;
return hit;
}
std::vector<Vector3>::const_iterator vertices;
std::vector<MeshTriangle>::const_iterator triangles;
bool hit;
};
bool GroupModel::IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const
{
if (!triangles.size())
return false;
GModelRayCallback callback(triangles, vertices);
meshTree.intersectRay(ray, callback, distance, stopAtFirstHit);
return callback.hit;
}
bool GroupModel::IsInsideObject(const Vector3 &pos, const Vector3 &down, float &z_dist) const
{
if (!triangles.size() || !iBound.contains(pos))
return false;
GModelRayCallback callback(triangles, vertices);
Vector3 rPos = pos - 0.1f * down;
float dist = G3D::inf();
G3D::Ray ray(rPos, down);
bool hit = IntersectRay(ray, dist, false);
if (hit)
z_dist = dist - 0.1f;
return hit;
}
bool GroupModel::GetLiquidLevel(const Vector3 &pos, float &liqHeight) const
{
if (iLiquid)
return iLiquid->GetLiquidHeight(pos, liqHeight);
return false;
}
uint32 GroupModel::GetLiquidType() const
{
// convert to type mask, matching MAP_LIQUID_TYPE_* defines in Map.h
if (iLiquid)
return (1 << iLiquid->GetType());
return 0;
}
// ===================== WorldModel ==================================
void WorldModel::setGroupModels(std::vector<GroupModel> &models)
{
groupModels.swap(models);
groupTree.build(groupModels, BoundsTrait<GroupModel>::getBounds, 1);
}
struct WModelRayCallBack
{
WModelRayCallBack(const std::vector<GroupModel> &mod): models(mod.begin()), hit(false) {}
bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool pStopAtFirstHit)
{
bool result = models[entry].IntersectRay(ray, distance, pStopAtFirstHit);
if (result) hit=true;
return hit;
}
std::vector<GroupModel>::const_iterator models;
bool hit;
};
bool WorldModel::IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const
{
// small M2 workaround, maybe better make separate class with virtual intersection funcs
// in any case, there's no need to use a bound tree if we only have one submodel
if (groupModels.size() == 1)
return groupModels[0].IntersectRay(ray, distance, stopAtFirstHit);
WModelRayCallBack isc(groupModels);
groupTree.intersectRay(ray, isc, distance, stopAtFirstHit);
return isc.hit;
}
class WModelAreaCallback {
public:
WModelAreaCallback(const std::vector<GroupModel> &vals, const Vector3 &down):
prims(vals.begin()), hit(vals.end()), minVol(G3D::inf()), zDist(G3D::inf()), zVec(down) {}
std::vector<GroupModel>::const_iterator prims;
std::vector<GroupModel>::const_iterator hit;
float minVol;
float zDist;
Vector3 zVec;
void operator()(const Vector3& point, uint32 entry)
{
float group_Z;
//float pVol = prims[entry].GetBound().volume();
//if(pVol < minVol)
//{
/* if (prims[entry].iBound.contains(point)) */
if (prims[entry].IsInsideObject(point, zVec, group_Z))
{
//minVol = pVol;
//hit = prims + entry;
if (group_Z < zDist)
{
zDist = group_Z;
hit = prims + entry;
}
#ifdef VMAP_DEBUG
const GroupModel &gm = prims[entry];
printf("%10u %8X %7.3f,%7.3f,%7.3f | %7.3f,%7.3f,%7.3f | z=%f, p_z=%f\n", gm.GetWmoID(), gm.GetMogpFlags(),
gm.GetBound().low().x, gm.GetBound().low().y, gm.GetBound().low().z,
gm.GetBound().high().x, gm.GetBound().high().y, gm.GetBound().high().z, group_Z, point.z);
#endif
}
//}
//std::cout << "trying to intersect '" << prims[entry].name << "'\n";
}
};
bool WorldModel::IntersectPoint(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, AreaInfo &info) const
{
if (!groupModels.size())
return false;
WModelAreaCallback callback(groupModels, down);
groupTree.intersectPoint(p, callback);
if (callback.hit != groupModels.end())
{
info.rootId = RootWMOID;
info.groupId = callback.hit->GetWmoID();
info.flags = callback.hit->GetMogpFlags();
info.result = true;
dist = callback.zDist;
return true;
}
return false;
}
bool WorldModel::GetLocationInfo(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, LocationInfo &info) const
{
if (!groupModels.size())
return false;
WModelAreaCallback callback(groupModels, down);
groupTree.intersectPoint(p, callback);
if (callback.hit != groupModels.end())
{
info.hitModel = &(*callback.hit);
dist = callback.zDist;
return true;
}
return false;
}
bool WorldModel::writeFile(const std::string &filename)
{
FILE *wf = fopen(filename.c_str(), "wb");
if (!wf)
return false;
bool result = true;
uint32 chunkSize, count;
result = fwrite(VMAP_MAGIC,1,8,wf) == 8;
if (result && fwrite("WMOD", 1, 4, wf) != 4) result = false;
chunkSize = sizeof(uint32) + sizeof(uint32);
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&RootWMOID, sizeof(uint32), 1, wf) != 1) result = false;
// write group models
count=groupModels.size();
if (count)
{
if (result && fwrite("GMOD", 1, 4, wf) != 4) result = false;
//chunkSize = sizeof(uint32)+ sizeof(GroupModel)*count;
//if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) result = false;
for (uint32 i=0; i<groupModels.size() && result; ++i)
result = groupModels[i].writeToFile(wf);
// write group BIH
if (result && fwrite("GBIH", 1, 4, wf) != 4) result = false;
if (result) result = groupTree.writeToFile(wf);
}
fclose(wf);
return result;
}
bool WorldModel::readFile(const std::string &filename)
{
FILE *rf = fopen(filename.c_str(), "rb");
if (!rf)
return false;
bool result = true;
uint32 chunkSize, count;
char chunk[8]; // Ignore the added magic header
if (!readChunk(rf, chunk, VMAP_MAGIC, 8)) result = false;
if (result && !readChunk(rf, chunk, "WMOD", 4)) result = false;
if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&RootWMOID, sizeof(uint32), 1, rf) != 1) result = false;
// read group models
if (result && readChunk(rf, chunk, "GMOD", 4))
{
//if (fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&count, sizeof(uint32), 1, rf) != 1) result = false;
if (result) groupModels.resize(count);
//if (result && fread(&groupModels[0], sizeof(GroupModel), count, rf) != count) result = false;
for (uint32 i=0; i<count && result; ++i)
result = groupModels[i].readFromFile(rf);
// read group BIH
if (result && !readChunk(rf, chunk, "GBIH", 4)) result = false;
if (result) result = groupTree.readFromFile(rf);
}
fclose(rf);
return result;
}
}
@@ -1,123 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* 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.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _WORLDMODEL_H
#define _WORLDMODEL_H
#include <G3D/HashTrait.h>
#include <G3D/Vector3.h>
#include <G3D/AABox.h>
#include <G3D/Ray.h>
#include "BIH.h"
#include "Platform/Define.h"
namespace VMAP
{
class TreeNode;
struct AreaInfo;
struct LocationInfo;
class MeshTriangle
{
public:
MeshTriangle(){};
MeshTriangle(uint32 na, uint32 nb, uint32 nc): idx0(na), idx1(nb), idx2(nc) {};
uint32 idx0;
uint32 idx1;
uint32 idx2;
};
class WmoLiquid
{
public:
WmoLiquid(uint32 width, uint32 height, const Vector3 &corner, uint32 type);
WmoLiquid(const WmoLiquid &other);
~WmoLiquid();
WmoLiquid& operator=(const WmoLiquid &other);
bool GetLiquidHeight(const Vector3 &pos, float &liqHeight) const;
uint32 GetType() const { return iType; }
float *GetHeightStorage() { return iHeight; }
uint8 *GetFlagsStorage() { return iFlags; }
uint32 GetFileSize();
bool writeToFile(FILE *wf);
static bool readFromFile(FILE *rf, WmoLiquid *&liquid);
private:
WmoLiquid(): iHeight(0), iFlags(0) {};
uint32 iTilesX; //!< number of tiles in x direction, each
uint32 iTilesY;
Vector3 iCorner; //!< the lower corner
uint32 iType; //!< liquid type
float *iHeight; //!< (tilesX + 1)*(tilesY + 1) height values
uint8 *iFlags; //!< info if liquid tile is used
};
/*! holding additional info for WMO group files */
class GroupModel
{
public:
GroupModel(): iLiquid(0) {}
GroupModel(const GroupModel &other);
GroupModel(uint32 mogpFlags, uint32 groupWMOID, const AABox &bound):
iBound(bound), iMogpFlags(mogpFlags), iGroupWMOID(groupWMOID), iLiquid(0) {}
~GroupModel() { delete iLiquid; }
//! pass mesh data to object and create BIH. Passed vectors get get swapped with old geometry!
void setMeshData(std::vector<Vector3> &vert, std::vector<MeshTriangle> &tri);
void setLiquidData(WmoLiquid *liquid) { iLiquid = liquid; }
bool IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const;
bool IsInsideObject(const Vector3 &pos, const Vector3 &down, float &z_dist) const;
bool GetLiquidLevel(const Vector3 &pos, float &liqHeight) const;
uint32 GetLiquidType() const;
bool writeToFile(FILE *wf);
bool readFromFile(FILE *rf);
const G3D::AABox& GetBound() const { return iBound; }
uint32 GetMogpFlags() const { return iMogpFlags; }
uint32 GetWmoID() const { return iGroupWMOID; }
protected:
G3D::AABox iBound;
uint32 iMogpFlags;// 0x8 outdor; 0x2000 indoor
uint32 iGroupWMOID;
std::vector<Vector3> vertices;
std::vector<MeshTriangle> triangles;
BIH meshTree;
WmoLiquid *iLiquid;
};
/*! Holds a model (converted M2 or WMO) in its original coordinate space */
class WorldModel
{
public:
WorldModel(): RootWMOID(0) {}
//! pass group models to WorldModel and create BIH. Passed vector is swapped with old geometry!
void setGroupModels(std::vector<GroupModel> &models);
void setRootWmoID(uint32 id) { RootWMOID = id; }
bool IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const;
bool IntersectPoint(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, AreaInfo &info) const;
bool GetLocationInfo(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, LocationInfo &info) const;
bool writeFile(const std::string &filename);
bool readFile(const std::string &filename);
protected:
uint32 RootWMOID;
std::vector<GroupModel> groupModels;
BIH groupTree;
};
} // namespace VMAP
#endif // _WORLDMODEL_H