//////////////////////////////////////////////////////////////////////////////// // Scorched3D (c) 2000-2003 // // This file is part of Scorched3D. // // Scorched3D 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. // // Scorched3D 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 Scorched3D; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //////////////////////////////////////////////////////////////////////////////// #include #include #include #include static const int minMapShift = 3; HeightMap::HeightMap() : hMap_(0), normals_(0), minMap_(0), maxMap_(0), backupMap_(0), nvec(0.0f, 0.0f, 1.0f) { } HeightMap::~HeightMap() { delete [] hMap_; delete [] normals_; delete [] minMap_; delete [] maxMap_; delete [] backupMap_; } void HeightMap::create(const int width, const int height) { width_ = width; height_ = height; delete [] hMap_; delete [] normals_; delete [] backupMap_; hMap_ = new float[(width_ + 1) * (height_ + 1)]; normals_ = new Vector[(width_ + 1) * (height_ + 1)]; backupMap_ = new float[(width_ + 1) * (height_ + 1)]; delete [] minMap_; delete [] maxMap_; minWidth_ = width >> minMapShift; minHeight_ = height >> minMapShift; minMap_ = new float[(minWidth_ + 1) * (minHeight_ + 1)]; maxMap_ = new float[(minWidth_ + 1) * (minHeight_ + 1)]; reset(); } void HeightMap::backup() { memcpy(backupMap_, hMap_, sizeof(float) * (width_ + 1) * (height_ + 1)); } void HeightMap::reset() { memset(hMap_, 0, sizeof(float) * (width_ + 1) * (height_ + 1)); memset(backupMap_, 0, sizeof(float) * (width_ + 1) * (height_ + 1)); memset(normals_, 0, sizeof(Vector) * (width_ + 1) * (height_ + 1)); for (int i=0; i<(minWidth_ + 1) * (minHeight_ + 1); i++) minMap_[i] = FLT_MAX; for (int i=0; i<(minWidth_ + 1) * (minHeight_ + 1); i++) maxMap_[i] = 0.0f; } bool HeightMap::getVector(Vector &vec, int x, int y) { if (x < 0 || y < 0 || x>width_ || y>height_) return false; vec[0] = (float) x; vec[1] = (float) y; vec[2] = getHeight(x,y); return true; } void HeightMap::getVectorPos(int pos, int &x, int &y, int dist) { switch (pos) { case 0: x=-dist; y=0; break; case 1: x=0; y=dist; break; case 2: x=dist; y=0; break; default: x=0; y=-dist; } } void HeightMap::generateNormals(int minX, int maxX, int minY, int maxY, ProgressCounter *counter) { if (counter) counter->setNewOp("Normals"); for (int x=minX; x<=maxX; x++) { if (counter) counter->setNewPercentage((100.0f * float(x - minX)) / float(maxX - minX)); for (int y=minY; y<=maxY; y++) { static Vector C; if (!getVector(C, x, y)) continue; static Vector total; total.zero(); int times = 0; for (int dist=1; dist<=3; dist+=2) { for (int a=0, b=1; a<4; a++, b++) { if (b>3) b=0; static Vector A; int aPosX, aPosY; getVectorPos(a, aPosX, aPosY, dist); if (!getVector(A, aPosX + x, aPosY + y)) continue; static Vector B; int bPosX, bPosY; getVectorPos(b, bPosX, bPosY, dist); if (!getVector(B, bPosX + x, bPosY + y)) continue; A-=C; B.StoreInvert(); B+=C; A *= B; A.StoreNormalize(); total += A; times += 1; } if (times > 4) break; } getNormal(x, y) = total / (float) times; } } } float HeightMap::getDist(Vector &start, Vector &dir, Vector &pos) { Vector dir2 = dir.get2DPerp(); float u1 = dir.dotP(pos - start)/dir.dotP(dir); Vector pt = dir * u1 + start; return (pos - pt).Magnitude(); } bool HeightMap::getIntersect(Line &line, Vector &intersect) { Vector direction = -((Vector &)line.getDirection()).Normalize(); Vector start = line.getEnd(); for (int i=0; i<1000; i++) { if (getHeight((int) start[0], (int) start[1]) > start[2]) { if (start[0] < 0 || start[0] > getMapWidth() || start[1] < 0 || start[1] > getMapHeight()) { return false; } intersect = start; return true; } start += direction; } return false; /* const float maxFloat = -999999; const int searchSquareWidth_ = 3; float maxdist = maxFloat; for (int x=0; x maxdist) { maxdist = dist; intersect = newInter; } } if (triB.rayIntersect(direction, newInter, interN, dist, true)) { if (dist > maxdist) { maxdist = dist; intersect = newInter; } } } } return (maxdist != maxFloat);*/ } float HeightMap::getInterpHeight(float w, float h) { int ihx = (int) w; int ihy = (int) h; int ihx2 = ihx+1; int ihy2 = ihy+1; float fhx = w - (float) ihx; float fhy = h - (float) ihy; float heightA = getHeight(ihx, ihy); float heightB = getHeight(ihx, ihy2); float heightC = getHeight(ihx2, ihy); float heightD = getHeight(ihx2, ihy2); float heightDiffAB = heightB-heightA; float heightDiffCD = heightD-heightC; float heightE = heightA + (heightDiffAB * fhy); float heightF = heightC + (heightDiffCD * fhy); float heightDiffEF = heightF - heightE; float height = heightE + (heightDiffEF * fhx); return height; } void HeightMap::getInterpNormal(float w, float h, Vector &normal) { int ihx = (int) w; int ihy = (int) h; int ihx2 = ihx+1; int ihy2 = ihy+1; float fhx = w - (float) ihx; float fhy = h - (float) ihy; Vector &normalA = getNormal(ihx, ihy); Vector &normalB = getNormal(ihx, ihy2); Vector &normalC = getNormal(ihx2, ihy); Vector &normalD = getNormal(ihx2, ihy2); static Vector normalDiffAB; normalDiffAB = normalB; normalDiffAB -= normalA; normalDiffAB *= fhy; static Vector normalDiffCD; normalDiffCD = normalD; normalDiffCD -= normalC; normalDiffCD *= fhy; static Vector normalE; normalE = normalA; normalE += normalDiffAB; static Vector normalF; normalF = normalC; normalF += normalDiffCD; static Vector normalDiffEF; normalDiffEF = normalF; normalDiffEF -= normalE; normalDiffEF *= fhx; normal = normalE; normal += normalDiffEF; } void HeightMap::setHeight(int w, int h, float height) { DIALOG_ASSERT(w >= 0 && h >= 0 && w<=width_ && h<=height_); hMap_[(width_+1) * h + w] = height; int newW = w >> minMapShift; int newH = h >> minMapShift; DIALOG_ASSERT(newW >= 0 && newH >= 0 && newW<=minWidth_ && newH<=minHeight_); int minOffSet = (minWidth_+1) * newH + newW; float *minHeight = &minMap_[minOffSet]; if (*minHeight > height) { *minHeight = height; } float *maxHeight = &maxMap_[minOffSet]; if (*maxHeight < height) { *maxHeight = height; } } void HeightMap::resetMinHeight() { for (int i=0; i<(minWidth_ + 1) * (minHeight_ + 1); i++) minMap_[i] = FLT_MAX; for (int i=0; i<(minWidth_ + 1) * (minHeight_ + 1); i++) maxMap_[i] = 0.0f; for (int h=0; h> minMapShift; int newH = h >> minMapShift; DIALOG_ASSERT(newW >= 0 && newH >= 0 && newW<=minWidth_ && newH<=minHeight_); int minOffSet = (minWidth_+1) * newH + newW; float *minHeight = &minMap_[minOffSet]; if (*minHeight > height) { *minHeight = height; } float *maxHeight = &maxMap_[minOffSet]; if (*maxHeight < height) { *maxHeight = height; } } } } float HeightMap::getMinHeight(int w, int h) { DIALOG_ASSERT(w >= 0 && h >= 0 && w<=minWidth_ && h<=minHeight_); float minHeight = minMap_[(minWidth_+1) * h + w]; return minHeight; } float HeightMap::getMaxHeight(int w, int h) { DIALOG_ASSERT(w >= 0 && h >= 0 && w<=minWidth_ && h<=minHeight_); float maxHeight = maxMap_[(minWidth_+1) * h + w]; return maxHeight; }