//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "terrain/terrData.h" #include "math/mMath.h" #include "dgl/dgl.h" #include "console/console.h" #include "console/consoleTypes.h" #include "dgl/gBitmap.h" #include "dgl/gTexManager.h" #include "terrain/terrRender.h" #include "sceneGraph/sceneGraph.h" U32 TerrainRender::TestSquareLights(GridSquare *sq, S32 level, Point2I pos, U32 lightMask) { U32 retMask = 0; F32 blockX = pos.x * mSquareSize + mBlockPos.x; F32 blockY = pos.y * mSquareSize + mBlockPos.y; F32 blockZ = fixedToFloat(sq->minHeight); F32 blockSize = mSquareSize * (1 << level); F32 blockHeight = fixedToFloat(sq->maxHeight - sq->minHeight); Point3F vec; for(S32 i = 0; (lightMask >> i) != 0; i++) { if(lightMask & (1 << i)) { Point3F *pos = &mTerrainLights[i].pos; // test the visibility of this light to box // find closest point on box to light and test if(pos->z < blockZ) vec.z = blockZ - pos->z; else if(pos->z > blockZ + blockHeight) vec.z = pos->z - (blockZ + blockHeight); else vec.z = 0; if(pos->x < blockX) vec.x = blockX - pos->x; else if(pos->x > blockX + blockSize) vec.x = pos->x - (blockX + blockSize); else vec.x = 0; if(pos->y < blockY) vec.y = blockY - pos->y; else if(pos->y > blockY + blockSize) vec.y = pos->y - (blockY + blockSize); else vec.y = 0; F32 dist = vec.len(); if(dist < mTerrainLights[i].radius) retMask |= (1 << i); } } return retMask; } void TerrainRender::buildLightArray() { static LightInfoList lights; lights.clear(); gClientSceneGraph->getLightManager()->sgGetBestLights(lights); // create terrain lights from these... U32 curIndex = 0; for(U32 i = 0; i < lights.size(); i++) { if(lights[i]->mType != LightInfo::Point) continue; if(!lights[i]->sgAllowDiffuseZoneLighting(0)) continue; // set the 'fo TerrLightInfo & info = mTerrainLights[curIndex++]; mCurrentBlock->getWorldTransform().mulP(lights[i]->mPos, &info.pos); info.radius = lights[i]->mRadius; //jff info.radiusSquared = info.radius * info.radius; // info.r = lights[i]->mColor.red; info.g = lights[i]->mColor.green; info.b = lights[i]->mColor.blue; Point3F dVec = mCamPos - lights[i]->mPos; info.distSquared = mDot(dVec, dVec); } mDynamicLightCount = curIndex; } static U16 convertColor(ColorF color) { if(color.red > 1) color.red = 1; if(color.green > 1) color.green = 1; if(color.blue > 1) color.blue = 1; return (U32(color.blue * 31) << 11) | (U32(color.green * 31) << 6) | (U32(color.red * 31) << 1) | 1; } void TerrainBlock::relight(const ColorF &lightColor, const ColorF &ambient, const Point3F &lightDir) { if(lightDir.x == 0 && lightDir.y == 0) return; if(!lightMap) return; S32 generateLevel = Con::getIntVariable("$pref::sceneLighting::terrainGenerateLevel", 0); generateLevel = mClamp(generateLevel, 0, 4); U32 generateDim = TerrainBlock::LightmapSize << generateLevel; U32 generateShift = TerrainBlock::LightmapShift + generateLevel; U32 generateMask = generateDim - 1; F32 zStep; F32 frac; Point2I blockColStep; Point2I blockRowStep; Point2I blockFirstPos; Point2I lmapFirstPos; F32 terrainDim = F32(getSquareSize()) * F32(TerrainBlock::BlockSize); F32 stepSize = F32(getSquareSize()) / F32(generateDim / TerrainBlock::BlockSize); if(mFabs(lightDir.x) >= mFabs(lightDir.y)) { if(lightDir.x > 0) { zStep = lightDir.z / lightDir.x; frac = lightDir.y / lightDir.x; blockColStep.set(1, 0); blockRowStep.set(0, 1); blockFirstPos.set(0, 0); lmapFirstPos.set(0, 0); } else { zStep = -lightDir.z / lightDir.x; frac = -lightDir.y / lightDir.x; blockColStep.set(-1, 0); blockRowStep.set(0, 1); blockFirstPos.set(255, 0); lmapFirstPos.set(TerrainBlock::LightmapSize-1, 0); } } else { if(lightDir.y > 0) { zStep = lightDir.z / lightDir.y; frac = lightDir.x / lightDir.y; blockColStep.set(0, 1); blockRowStep.set(1, 0); blockFirstPos.set(0, 0); lmapFirstPos.set(0, 0); } else { zStep = -lightDir.z / lightDir.y; frac = -lightDir.x / lightDir.y; blockColStep.set(0, -1); blockRowStep.set(1, 0); blockFirstPos.set(0, 255); lmapFirstPos.set(0, TerrainBlock::LightmapSize-1); } } zStep *= stepSize; F32 * heightArray = new F32[generateDim]; S32 fracStep = -1; if(frac < 0) { fracStep = 1; frac = -frac; } F32 * nextHeightArray = new F32[generateDim]; F32 oneMinusFrac = 1 - frac; U32 blockShift = generateShift - TerrainBlock::BlockShift; U32 lightmapShift = generateShift - TerrainBlock::LightmapShift; U32 blockStep = 1 << blockShift; U32 blockMask = (1 << blockShift) - 1; U32 lightmapMask = (1 << lightmapShift) - 1; Point2I bp = blockFirstPos; F32 terrainHeights[2][TerrainBlock::BlockSize]; U32 i; // get first set of heights for(i = 0; i < TerrainBlock::BlockSize; i++, bp += blockRowStep) terrainHeights[0][i] = fixedToFloat(getHeight(bp.x, bp.y)); // get second set of heights bp = blockFirstPos + blockColStep; for(i = 0; i < TerrainBlock::BlockSize; i++, bp += blockRowStep) terrainHeights[1][i] = fixedToFloat(getHeight(bp.x, bp.y)); F32 * pTerrainHeights[2]; pTerrainHeights[0] = static_cast(terrainHeights[0]); pTerrainHeights[1] = static_cast(terrainHeights[1]); F32 heightStep = 1.f / blockStep; F32 terrainZRowStep[2][TerrainBlock::BlockSize]; F32 terrainZColStep[TerrainBlock::BlockSize]; // fill in the row steps for(i = 0; i < TerrainBlock::BlockSize; i++) { terrainZRowStep[0][i] = (terrainHeights[0][(i+1) & TerrainBlock::BlockMask] - terrainHeights[0][i]) * heightStep; terrainZRowStep[1][i] = (terrainHeights[1][(i+1) & TerrainBlock::BlockMask] - terrainHeights[1][i]) * heightStep; terrainZColStep[i] = (terrainHeights[1][i] - terrainHeights[0][i]) * heightStep; } // get first row of process heights for(i = 0; i < generateDim; i++) { U32 bi = i >> blockShift; heightArray[i] = terrainHeights[0][bi] + (i & blockMask) * terrainZRowStep[0][bi]; } bp = blockFirstPos; if(generateDim == TerrainBlock::BlockSize) bp += blockColStep; // generate the initial run U32 x, y; for(x = 1; x < generateDim; x++) { U32 xmask = x & blockMask; // generate new height step rows? if(!xmask) { F32 * tmp = pTerrainHeights[0]; pTerrainHeights[0] = pTerrainHeights[1]; pTerrainHeights[1] = tmp; bp += blockColStep; Point2I bwalk = bp; for(i = 0; i < TerrainBlock::BlockSize; i++, bwalk += blockRowStep) pTerrainHeights[1][i] = fixedToFloat(getHeight(bwalk.x, bwalk.y)); // fill in the row steps for(i = 0; i < TerrainBlock::BlockSize; i++) { terrainZRowStep[0][i] = (pTerrainHeights[0][(i+1) & TerrainBlock::BlockMask] - pTerrainHeights[0][i]) * heightStep; terrainZRowStep[1][i] = (pTerrainHeights[1][(i+1) & TerrainBlock::BlockMask] - pTerrainHeights[1][i]) * heightStep; terrainZColStep[i] = (pTerrainHeights[1][i] - pTerrainHeights[0][i]) * heightStep; } } Point2I bwalk = bp - blockRowStep; for(y = 0; y < generateDim; y++) { U32 ymask = y & blockMask; if(!ymask) bwalk += blockRowStep; U32 bi = y >> blockShift; U32 binext = (bi + 1) & TerrainBlock::BlockMask; F32 height; // 135? if((bwalk.x ^ bwalk.y) & 1) { U32 xsub = blockStep - xmask; if(xsub > ymask) // bottom height = pTerrainHeights[0][bi] + xmask * terrainZColStep[bi] + ymask * terrainZRowStep[0][bi]; else // top height = pTerrainHeights[1][bi] - xsub * terrainZColStep[binext] + ymask * terrainZRowStep[1][bi]; } else { if(xmask > ymask) // bottom height = pTerrainHeights[0][bi] + xmask * terrainZColStep[bi] + ymask * terrainZRowStep[1][bi]; else // top height = pTerrainHeights[0][bi] + xmask * terrainZColStep[binext] + ymask * terrainZRowStep[0][bi]; } F32 intHeight = heightArray[y] * oneMinusFrac + heightArray[(y + fracStep) & generateMask] * frac + zStep; nextHeightArray[y] = getMax(height, intHeight); } // swap the height rows for(y = 0; y < generateDim; y++) heightArray[y] = nextHeightArray[y]; } F32 squareSize = getSquareSize(); F32 squaredSquareSize = squareSize * squareSize; F32 lexelDim = squareSize * F32(TerrainBlock::BlockSize) / F32(TerrainBlock::LightmapSize); // calculate normal runs Point3F normals[2][TerrainBlock::BlockSize]; Point3F * pNormals[2]; pNormals[0] = static_cast(normals[0]); pNormals[1] = static_cast(normals[1]); // calculate the normal lookup table F32 * normTable = new F32 [blockStep * blockStep * 4]; Point2F corners[4] = { Point2F(0.f, 0.f), Point2F(1.f, 0.f), Point2F(1.f, 1.f), Point2F(0.f, 1.f) }; U32 idx = 0; F32 step = 1.f / blockStep; F32 halfStep = step / 2.f; Point2F pos(halfStep, halfStep); // fill it for(x = 0; x < blockStep; x++, pos.x += step, pos.y = halfStep) for(y = 0; y < blockStep; y++, pos.y += step) for(i = 0; i < 4; i++, idx++) normTable[idx] = 1.f - getMin(Point2F(pos - corners[i]).len(), 1.f); // fill first column bp = blockFirstPos; for(x = 0; x < TerrainBlock::BlockSize; x++) { terrainHeights[0][x] = fixedToFloat(getHeight(bp.x, bp.y)); Point2F pos(bp.x * squareSize, bp.y * squareSize); getNormal(pos, &pNormals[1][x]); bp += blockRowStep; } // get swapped on first pass pTerrainHeights[0] = static_cast(terrainHeights[1]); pTerrainHeights[1] = static_cast(terrainHeights[0]); ColorF colors[TerrainBlock::LightmapSize]; F32 ratio = F32(1 << lightmapShift); F32 inverseRatioSquared = 1.f / (ratio * ratio); // walk it... bp = blockFirstPos - blockColStep; Point2I lp = lmapFirstPos - blockColStep; for(x = 0; x < generateDim; x++) { U32 xmask = x & blockMask; // process lightmap? if(!(x & lightmapMask)) { dMemset(colors, 0, sizeof(ColorF) * TerrainBlock::LightmapSize); lp += blockColStep; } // generate new runs? if(!xmask) { bp += blockColStep; // do the normals Point3F * temp = pNormals[0]; pNormals[0] = pNormals[1]; pNormals[1] = temp; // fill the row Point2I bwalk = bp + blockColStep; for(i = 0; i < TerrainBlock::BlockSize; i++) { Point2F pos(bwalk.x * squareSize, bwalk.y * squareSize); getNormal(pos, &pNormals[1][i]); bwalk += blockRowStep; } // do the heights F32 * tmp = pTerrainHeights[0]; pTerrainHeights[0] = pTerrainHeights[1]; pTerrainHeights[1] = tmp; bwalk = bp + blockColStep; for(i = 0; i < TerrainBlock::BlockSize; i++, bwalk += blockRowStep) pTerrainHeights[1][i] = fixedToFloat(getHeight(bwalk.x, bwalk.y)); // fill in the row steps for(i = 0; i < TerrainBlock::BlockSize; i++) { terrainZRowStep[0][i] = (pTerrainHeights[0][(i+1) & TerrainBlock::BlockMask] - pTerrainHeights[0][i]) * heightStep; terrainZRowStep[1][i] = (pTerrainHeights[1][(i+1) & TerrainBlock::BlockMask] - pTerrainHeights[1][i]) * heightStep; terrainZColStep[i] = (pTerrainHeights[1][i] - pTerrainHeights[0][i]) * heightStep; } } Point2I bwalk = bp - blockRowStep; for(y = 0; y < generateDim; y++) { U32 ymask = y & blockMask; if(!ymask) bwalk += blockRowStep; U32 bi = y >> blockShift; U32 binext = (bi + 1) & TerrainBlock::BlockMask; F32 height; // 135? if((bwalk.x ^ bwalk.y) & 1) { U32 xsub = blockStep - xmask; if(xsub > ymask) // bottom height = pTerrainHeights[0][bi] + xmask * terrainZColStep[bi] + ymask * terrainZRowStep[0][bi]; else // top height = pTerrainHeights[1][bi] - xsub * terrainZColStep[binext] + ymask * terrainZRowStep[1][bi]; } else { if(xmask > ymask) // bottom height = pTerrainHeights[0][bi] + xmask * terrainZColStep[bi] + ymask * terrainZRowStep[1][bi]; else // top height = pTerrainHeights[0][bi] + xmask * terrainZColStep[binext] + ymask * terrainZRowStep[0][bi]; } F32 intHeight = heightArray[y] * oneMinusFrac + heightArray[(y + fracStep) & generateMask] * frac + zStep; ColorF & col = colors[y >> lightmapShift]; // non shadowed? if(height >= intHeight) { U32 idx = (xmask + (ymask << blockShift)) << 2; Point3F normal; normal = pNormals[0][bi] * normTable[idx++]; normal += pNormals[0][binext] * normTable[idx++]; normal += pNormals[1][binext] * normTable[idx++]; normal += pNormals[1][bi] * normTable[idx]; normal.normalize(); nextHeightArray[y] = height; F32 colorScale = mDot(normal, lightDir); if(colorScale >= 0) col += ambient; else col += (ambient + lightColor * -colorScale); } else { nextHeightArray[y] = intHeight; col += ambient; } } for(y = 0; y < generateDim; y++) heightArray[y] = nextHeightArray[y]; // do some lighting stuff? if(!((x+1) & lightmapMask)) { Point2I lwalk = lp; U32 mask = TerrainBlock::LightmapSize - 1; for(i = 0; i < TerrainBlock::LightmapSize; i++) { U16 * ptr = (U16*)lightMap->getAddress(lp.x & mask, lp.y & mask); *ptr = convertColor(colors[i]); lp += blockRowStep; } } } delete [] normTable; delete [] heightArray; delete [] nextHeightArray; }