From 2e1f97f3d52e00329fb043d2e95ebe27eb9b48ea Mon Sep 17 00:00:00 2001 From: nkorslund Date: Fri, 5 Jun 2009 06:10:32 +0000 Subject: [PATCH] - applied lighting patch from Chris Robinson - terrain gen not finished git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@120 ea6a568a-9f4f-0410-981a-c910a81bb256 --- core/config.d | 36 ++ nif/property.d | 8 + ogre/cpp_interface.cpp | 56 +- ogre/ogre.d | 19 + terrain/generator.d | 1162 +++++++++++++++++++++++++++++++++++++++- terrain/terrain.d | 2 + util/cachefile.d | 48 ++ 7 files changed, 1306 insertions(+), 25 deletions(-) create mode 100644 util/cachefile.d diff --git a/core/config.d b/core/config.d index 5a9060d8c..bb44c89c4 100644 --- a/core/config.d +++ b/core/config.d @@ -39,6 +39,8 @@ import sound.audio; import input.keys; import input.ois; +import ogre.ogre; + ConfigManager config; /* @@ -167,6 +169,23 @@ struct ConfigManager float sfxVolume = saneVol(ini.getFloat("Sound", "SFX Volume", 0.5)); bool useMusic = ini.getBool("Sound", "Enable Music", true); + + lightConst = ini.getInt("LightAttenuation", "UseConstant", 0); + lightConstValue = ini.getFloat("LightAttenuation", "ConstantValue", 0.0); + + lightLinear = ini.getInt("LightAttenuation", "UseLinear", 1); + lightLinearMethod = ini.getInt("LightAttenuation", "LinearMethod", 1); + lightLinearValue = ini.getFloat("LightAttenuation", "LinearValue", 3.0); + lightLinearRadiusMult = ini.getFloat("LightAttenuation", "LinearRadiusMult", 1.0); + + lightQuadratic = ini.getInt("LightAttenuation", "UseQuadratic", 0); + lightQuadraticMethod = ini.getInt("LightAttenuation", "QuadraticMethod", 2); + lightQuadraticValue = ini.getFloat("LightAttenuation", "QuadraticValue", 16.0); + lightQuadraticRadiusMult = ini.getFloat("LightAttenuation", "QuadraticRadiusMult", 1.0); + + lightOutQuadInLin = ini.getInt("LightAttenuation", "OutQuadInLin", 0); + + *mouseSensX = ini.getFloat("Controls", "Mouse Sensitivity X", 0.2); *mouseSensY = ini.getFloat("Controls", "Mouse Sensitivity Y", 0.2); *flipMouseY = ini.getBool("Controls", "Flip Mouse Y Axis", false); @@ -360,6 +379,23 @@ struct ConfigManager writeFloat("SFX Volume", mo.getFloat("sfxVolume")); writeBool("Enable Music", mo.getBool("useMusic")); + section("LightAttenuation"); + comment("For constant attenuation"); + writeInt("UseConstant", lightConst); + writeFloat("ConstantValue", lightConstValue); + comment("For linear attenuation"); + writeInt("UseLinear", lightLinear); + writeInt("LinearMethod", lightLinearMethod); + writeFloat("LinearValue", lightLinearValue); + writeFloat("LinearRadiusMult", lightLinearRadiusMult); + comment("For quadratic attenuation"); + writeInt("UseQuadratic", lightQuadratic); + writeInt("QuadraticMethod", lightQuadraticMethod); + writeFloat("QuadraticValue", lightQuadraticValue); + writeFloat("QuadraticRadiusMult", lightQuadraticRadiusMult); + comment("For quadratic in exteriors and linear in interiors"); + writeInt("OutQuadInLin", lightOutQuadInLin); + section("Game Files"); foreach(int i, ref s; gameFiles) writeString(format("GameFile[%d]",i), s[esmDir.length..$]); diff --git a/nif/property.d b/nif/property.d index 9e605b619..cc4395c27 100644 --- a/nif/property.d +++ b/nif/property.d @@ -272,6 +272,14 @@ class NiAlphaProperty : Property Taken from: http://niftools.sourceforge.net/doc/nif/NiAlphaProperty.html + + Right now we only use standard alpha blending (see the Ogre code + that sets it up) and it appears that this is the only blending + used in the original game. Bloodmoon (along with several mods) do + however use other settings, such as discarding pixel values with + alpha < 1.0. This is faster because we don't have to mess with the + depth stuff like we did for blending. And OGRE has settings for + this too. */ // Tested against when certain flags are set (see above.) diff --git a/ogre/cpp_interface.cpp b/ogre/cpp_interface.cpp index 143b75550..3374f6066 100644 --- a/ogre/cpp_interface.cpp +++ b/ogre/cpp_interface.cpp @@ -21,6 +21,29 @@ */ +//----------------------------------------------------------------------- +// E X P O R T E D V A R I A B L E S +//----------------------------------------------------------------------- + +extern "C" +{ + int lightConst; + float lightConstValue; + + int lightLinear; + int lightLinearMethod; + float lightLinearValue; + float lightLinearRadiusMult; + + int lightQuadratic; + int lightQuadraticMethod; + float lightQuadraticValue; + float lightQuadraticRadiusMult; + + int lightOutQuadInLin; +} + + //----------------------------------------------------------------------- // E X P O R T E D F U N C T I O N S //----------------------------------------------------------------------- @@ -244,12 +267,37 @@ extern "C" Light* ogre_attachLight(char *name, SceneNode* base, float radius) { Light *l = mSceneMgr->createLight(name); - l->setDiffuseColour(r,g,b); - // This seems to look reasonably ok. - l->setAttenuation(3*radius, 0, 0, 12.0/(radius*radius)); - //l->setAttenuation(5*radius, 0, 0, 8.0/(radius*radius)); + radius /= 4.0f; + + float cval=0.0f, lval=0.0f, qval=0.0f; + if(lightConst) + cval = lightConstValue; + if(!lightOutQuadInLin) + { + if(lightLinear) + radius *= lightLinearRadiusMult; + if(lightQuadratic) + radius *= lightQuadraticRadiusMult; + + if(lightLinear) + lval = lightLinearValue / pow(radius, lightLinearMethod); + if(lightQuadratic) + qval = lightQuadraticValue / pow(radius, lightQuadraticMethod); + } + else + { + // FIXME: + // Do quadratic or linear, depending if we're in an exterior or interior + // cell, respectively. Ignore lightLinear and lightQuadratic. + } + + // The first parameter is a cutoff value on which meshes to + // light. If it's set to small, some meshes will end up 'flashing' + // in and out of light depending on the camera distance from the + // light. + l->setAttenuation(10*radius, cval, lval, qval); // base might be null, sometimes lights don't have meshes if(base) base->attachObject(l); diff --git a/ogre/ogre.d b/ogre/ogre.d index 4f220cd57..79fae93d5 100644 --- a/ogre/ogre.d +++ b/ogre/ogre.d @@ -36,6 +36,25 @@ import std.stdio; import esm.defs; + +extern(C) { +extern int lightConst; +extern float lightConstValue; + +extern int lightLinear; +extern int lightLinearMethod; +extern float lightLinearValue; +extern float lightLinearRadiusMult; + +extern int lightQuadratic; +extern int lightQuadraticMethod; +extern float lightQuadraticValue; +extern float lightQuadraticRadiusMult; + +extern int lightOutQuadInLin; +} + + class OgreException : Exception { this(char[] msg) {super("OgreException: " ~ msg);} diff --git a/terrain/generator.d b/terrain/generator.d index 4f62afd27..7e17d10cd 100644 --- a/terrain/generator.d +++ b/terrain/generator.d @@ -19,50 +19,1170 @@ version 3 along with this program. If not, see http://www.gnu.org/licenses/ . - */ +*/ // This module is responsible for generating the cache files. module terrain.generator; +/+ import std.stdio; -import std.file; -import monster.util.string; +import std.string; +import terrain.cachewriter; +import util.cachefile; + +const float TEX_SCALE = 1.0/16; char[] cacheDir = "cache/terrain/"; +// Interface to the ESM landscape data +MWLand mwland; + +int mCount; + +// Texture sizes for the various levels. For the most detailed level +// (level 1), this give the size of the alpha splatting textures rather +// than a final texture. +int[] texSizes; + +// Default textures +GenLevelResult[] defaults; + +CacheWriter cache; + void generate() { makePath(cacheDir); - terr_setCacheDir(cacheDir.ptr); + + cache.openFile(filename); + + // Find the maxiumum distance from (0,0) in any direction + int max = mwland.getMaxCoord(); + + // Round up to nearest binary + int depth=1; + while(max) + { + max >>= 1; + depth++; + assert(depth <= 8); + } + max = 1 << depth-1; + + // We already know the answers + assert(max == 32); + assert(depth == 6); + + // Set the texture sizes. TODO: These should be config options, + // perhaps - or maybe a result of some higher-level detail setting. + texSizes.resize(depth+1, 0); + texSizes[6] = 1024; + texSizes[5] = 512; + texSizes[4] = 256; + texSizes[3] = 256; + texSizes[2] = 256; + texSizes[1] = 64; + + // Set some general parameters for the runtime + cache.setParams(depth+1, texSizes[1]); + + // Create some common data first + writefln("Generating common data"); + genDefaults(); + genIndexData(); + + writefln("Generating quad data"); + GenLevelResult gen; + // Start at one level above the top, but don't generate a mesh for + // it + genLevel(depth+1, -max, -max, gen, false); + writefln("Writing index file"); + cache.finish(); + writefln("Pregeneration done. Results written to ", filename); +} + +// Generates the default texture images "2_default.png" etc +void genDefaults() +{ + int size = texSizes.length-1; + defaults.length = size; + + for(int i=1; i 2); + genLevel2Map(null, defaults[2]); + + for(int i=3; i=0&&v>=0); + assert(u<=1&&v<=1); + + *vertPtr++ = u; + *vertPtr++ = v; + } + assert(vertPtr-vertList.ptr == size); + // Store the buffer + cache.addVertexBuffer(lev,vertList); + } + + // Next up, triangle indices + int size = 64*64*6; + auto indList = new ushort[size]; + ushort *indPtr = indList.ptr; + + bool flag = false; + for ( int y = 0; y < 64; y++ ) + { + for ( int x = 0; x < 64; x++ ) + { + int line1 = y*65 + x; + int line2 = (y+1)*65 + x; + + if ( flag ) + { + *indPtr++ = line1; + *indPtr++ = line2; + *indPtr++ = line1 + 1; + + *indPtr++ = line1 + 1; + *indPtr++ = line2; + *indPtr++ = line2 + 1; + } + else + { + *indPtr++ = line1; + *indPtr++ = line2; + *indPtr++ = line2 + 1; + + *indPtr++ = line1; + *indPtr++ = line2 + 1; + *indPtr++ = line1 + 1; + } + flag = !flag; //flip tris for next time + } + flag = !flag; //flip tries for next row + } + assert(indPtr-indList.ptr==size); + + // The index buffers are the same for all levels + cache.addIndexBuffer(1,indList); + cache.addIndexBuffer(2,indList); + cache.addIndexBuffer(3,indList); + cache.addIndexBuffer(4,indList); + cache.addIndexBuffer(5,indList); + cache.addIndexBuffer(6,indList); +} + +void genLevel(int level, int X, int Y, ref GenLevelResult result, + bool makeData = true) +{ + result.quad.info.cellX = X; + result.quad.info.cellY = Y; + result.quad.info.level = level; + + assert(result.isEmpty); + + // Level 1 (most detailed) is handled differently from the + // other leves. + if(level == 1) + { + assert(makeData); + + if(!mwland.hasData(X,Y)) + // Oops, there's no data for this cell. Skip it. + return; + + // The mesh is generated in pieces rather than as one part. + genLevel1Meshes(result); + + // We also generate alpha maps instead of the actual textures. + genCellAlpha(result); + + if(!result.isEmpty()) + { + // Store the information we just created + assert(result.hasAlpha()); + cache.writeQuad(result.quad); + } + + return; + } + assert(level > 1); + + // Number of cells along one side in each sub-quad (not in this + // quad) + int cells = 1 << (level-2); + + // Call the sub-levels and store the result + GenLevelResult sub[4]; + genLevel(level-1, X, Y, sub[0]); // NW + genLevel(level-1, X+cells, Y, sub[1]); // NE + genLevel(level-1, X, Y+cells, sub[2]); // SW + genLevel(level-1, X+cells, Y+cells, sub[3]); // SE + + scope(exit) + { + foreach(ref s; sub) + s.kill(); + } + + // Mark the sub-quads that have data + bool anyUsed = false; + for(int i=0;i<4;i++) + { + bool used = !sub[i].isEmpty(); + result.quad.info.hasChild[i] = used; + anyUsed = anyUsed || used; + } + + if(!anyUsed) + { + // If our children are empty, then we are also empty. + assert(result.isEmpty()); + return; + } + + if(makeData) + { + if(level == 2) + // For level==2, generate a new texture from the alpha maps. + genLevel2Map(sub, result); + else + // Level 3+, merge the images from the previous levels + mergeMaps(sub, result); + + // Create the landscape mesh by merging the result from the + // children. + mergeMesh(sub, result); + } + + // Store the result in the cache file + cache.writeQuad(result.quad); +} + +// Generate mesh data for one cell +void genLevel1Meshes(ref GenLevelResult res) +{ + // Constants + const int intervals = 64; + const int vertNum = intervals+1; + const int vertSep = 128; + + // Allocate the mesh buffer + res.allocMesh(vertNum); + + int cellX = res.quad.info.cellX; + int cellY = res.quad.info.cellY; + assert(res.quad.info.level==1); + + MeshHolder *mh = &res.quad.meshes[0]; + MeshInfo *mi = &mh.info; + + mi.worldWidth = vertSep*intervals; + assert(mi.worldWidth == 8192); + + auto land = mwland.getData(cellX, cellY); + + byte[] heightData = land.heights; + byte[] normals = land.normals; + mi.heightOffset = land.heightOffset; + + float max=-1000000.0; + float min=1000000.0; + + byte *vertPtr = mh.vertexBuffer.ptr; + assert(vertPtr !is null); + + // Loop over all the vertices in the mesh + float rowheight = mi.heightOffset; + float height; + for(int y=0; y<65; y++) + for(int x=0; x<65; x++) + { + // Offset of this vertex within the source buffer + int offs=y*65+x; + + // The vertex data from the ESM + byte data = heightData[offs]; + + // Write the height byte + *vertPtr++ = data; + + // Calculate the height here, even though we don't store + // it. We use it to find the min and max values. + if(x == 0) + { + // Set the height to the row height + height = rowheight; + + // First value in each row adjusts the row height + rowheight += data; + } + // Adjust the accumulated height with the new data. The + // adjustment is a signed number. + height += data; + + // Calculate the min and max + max = height > max ? height : max; + min = height < min ? height : min; + + // Store the normals + for(int k=0; k<3; k++) + *vertPtr++ = normals[offs*3+k]; + } + + // Make sure we wrote exactly the right amount of data + assert(vertPtr-mh.vertexBuffer.ptr == + mh.vertexBuffer.length - 1); + + // Store the min/max values + mi.minHeight = min * 8; + mi.maxHeight = max * 8; +} + +// Generate the alpha splatting bitmap for one cell. +void genCellAlpha(ref GenLevelResult res) +{ + int cellX = res.quad.info.cellX; + int cellY = res.quad.info.cellY; + assert(res.quad.info.level == 1); + + // List of texture indices for this cell. A cell has 16x16 texture + // squares. + int ltex[16][16]; + + auto ltexData = mwland.getLTEXData(cellX, cellY); + + // A map from the global texture index to the local index for this + // cell. + int[int] textureMap; + + int texNum = 0; // Number of local indices + + // Loop through all the textures in the cell and get the indices + bool isDef = true; + for(int ty = 0; ty < 16; ty++) + for(int tx = 0; tx < 16; tx++) + { + // Get the texture in a given cell + char[] textureName = ltexData.getTexture(tx,ty); + + if(textureName == "") + textureName = "_land_default.dds"; + else + isDef = false; + + // Store the global index + int index = cache.addTexture(textureName); + ltex[ty][tx] = index; + + // Add the index to the map + if(!(index in textureMap)) + textureMap[index] = texNum++; + } + assert(texNum == textureMap.length); + + // If we only found default textures, exit now. + if(isDef) + return; + + int imageRes = texSizes[1]; + int dataSize = imageRes*imageRes; + + // Number of alpha pixels per texture square + int pps = imageRes/16; + + // Make sure there are at least as many alpha pixels as there are + // textures + assert(imageRes >= 16); + assert(imageRes%16 == 0); + assert(pps >= 1); + assert(texNum >= 1); + + // Allocate the alpha images + ubyte *uptr = res.allocAlphas(imageRes, texNum); + + assert(res.hasAlpha() && !res.isEmpty()); + + // Write the indices to the result list + foreach(int global, int local; textureMap) + res.setAlphaTex(local, global); + + // Loop over all textures again. This time, do alpha splatting. + for(int ty = 0; ty < 16; ty++) + for(int tx = 0; tx < 16; tx++) + { + // Get the local index for this square + int index = textures[ltex[ty][tx]]; + + // Get the offset of this square + long offs = index*dataSize + pps*(ty*imageRes + tx); + + // FIXME: Make real splatting later. This is just + // placeholder code. + + // Set alphas to full for this square + for(int y=0; ygetTechnique(0)->getPass(0); + np->setLightingEnabled(false); + np->createTextureUnitState("_land_default.dds") + ->setTextureScale(scale,scale); + + // List of resources created + std::list createdResources; + */ + + // Loop through all our textures + if(maps !is null) + foreach(int gIndex, LtexList inds; lmap) + { + char[] name = cache.getString(gIndex); + if ( name == "_land_default.dds" ) + continue; + + // Instead of passing 'mat', it's better to just make it + // global in C++ even if it's messy and definitely not thread + // safe. + auto pDest = cpp_makeAlphaLayer(mat, materialName ~ "_A_" ~ name); + + /* + // Create alpha map for this texture + std::string alphaName(materialName + "_A_" + tn); + Ogre::TexturePtr texPtr = Ogre::TextureManager::getSingleton(). + createManual(alphaName, + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, + 2*fromSize,2*fromSize, + 1,0, // depth, mipmaps + Ogre::PF_A8, // One-channel alpha + Ogre::TU_STATIC_WRITE_ONLY); + + createdResources.push_back(texPtr); + + Ogre::HardwarePixelBufferSharedPtr pixelBuffer = texPtr->getBuffer(); + pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD); + const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); + + Ogre::uint8* pDest = static_cast(pixelBox.data); + */ + + // Fill in the alpha values. TODO: Do all this with slices instead. + memset(pDest, 0, 4*fromSize*fromSize); + for(int i=0;i<4;i++) + { + // Does this sub-image have this texture? + if(inds[i] == -1) continue; + + assert(!maps[i].isEmpty()); + + // Find the right sub-texture in the alpha map + ubyte *from = maps[i].image.data + + (fromSize*fromSize)*inds[i]; + + // Find the right destination pointer + int x = i%2; + int y = i/2; + ubyte *to = pDest + x*fromSize + y*fromSize*fromSize*2; + + // Copy the rows one by one + for(int row = 0; row < fromSize; row++) + { + assert(to+fromSize <= pDest + 4*fromSize*fromSize); + memcpy(to, from, fromSize); + to += 2*fromSize; + from += fromSize; + } + } + + cpp_closeAlpha(name, scale); + /* + pixelBuffer->unlock(); + np = mp->getTechnique(0)->createPass(); + np->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA); + np->setLightingEnabled(false); + np->setDepthFunction(Ogre::CMPF_EQUAL); + + Ogre::TextureUnitState* tus = np->createTextureUnitState(alphaName); + tus->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP); + + tus->setAlphaOperation( Ogre::LBX_BLEND_TEXTURE_ALPHA, + Ogre::LBS_TEXTURE, + Ogre::LBS_TEXTURE); + tus->setColourOperationEx( Ogre::LBX_BLEND_DIFFUSE_ALPHA, + Ogre::LBS_TEXTURE, + Ogre::LBS_TEXTURE); + tus->setIsAlpha(true); + + tus = np->createTextureUnitState(tn); + tus->setColourOperationEx( Ogre::LBX_BLEND_DIFFUSE_ALPHA, + Ogre::LBS_TEXTURE, + Ogre::LBS_CURRENT); + + tus->setTextureScale(scale, scale); + */ + } + + // Create the result buffer + res.allocImage(toSize); + + // Output file name. TODO: The result structure can do this for us + // now, it knows both the level and the cell coords. Figure out + // what to do in the default case though. + int X = res.quad.info.cellX; + int Y = res.quad.info.cellY; + char[] outname = + "2_" ~ toString(X) ~ "_" ~ toString(Y) ~ ".png"; + + // Override for the default image + if(maps == NULL) + outname = "2_default.png"; + + outname = g_cacheDir + outname; + + // TODO: Store the file name in the cache (ie. in res), so we don't + // have to generate it at runtime. + + cpp_cleanupAlpha(outname); + + /* + Ogre::TexturePtr tex1 = getRenderedTexture(mp,materialName + "_T", + toSize,Ogre::PF_R8G8B8); + // Blit the texture over + tex1->getBuffer()->blitToMemory(res.image); + + // Clean up + Ogre::MaterialManager::getSingleton().remove(mp->getHandle()); + Ogre::TextureManager::getSingleton().remove(tex1->getHandle()); + const std::list::const_iterator iend = createdResources.end(); + for ( std::list::const_iterator itr = createdResources.begin(); + itr != iend; + ++itr) { + (*itr)->getCreator()->remove((*itr)->getHandle()); + } + + res.save(outname); + */ } -// Move elsewhere, make part of the general cache system later -void makeDir(char[] pt) +void mergeMaps(GenLevelResult *maps, ref GenLevelResult res) { - if(exists(pt)) + int level = res.quad.info.level; + + assert(texSizes.length > level); + assert(level > 2); + int fromSize = texSizes[level-1]; + int toSize = texSizes[level]; + + // Create a new image buffer large enough to hold the four + // sub textures + res.allocImage(fromSize*2); + + // Add the four sub-textures + for(int mi=0;mi<4;mi++) { - if(!isdir(pt)) - fail(pt ~ " is not a directory"); + // Need to do this in pure D. Oh well. + PixelBox src; + + // Use default texture if no source is present + if(maps == NULL || maps[mi].isEmpty()) + src = defaults[level-1].image; + else + src = maps[mi].image; + + // Find the sub-part of the destination buffer to write to + int x = (mi%2) * fromSize; + int y = (mi/2) * fromSize; + PixelBox dst = res.image.getSubVolume(Box(x,y,x+fromSize,y+fromSize)); + + // Copy the image to the box. Might as well rewrite this to do + // what we want. + copyBox(dst, src); } + + // Resize image if necessary + if(toSize != 2*fromSize) + res.resize(toSize); + + int X = res.quad.info.cellX; + int Y = res.quad.info.cellY; + + // Texture file name + char[] outname = res.getPNGName(maps==NULL); + + outname = g_cacheDir + outname; + + // Save the image + res.save(outname); +} + +/* +// Renders a material into a texture +Ogre::TexturePtr getRenderedTexture(Ogre::MaterialPtr mp, const std::string& name, + int texSize, Ogre::PixelFormat tt) +{ + TRACE("getRenderedTexture"); + + Ogre::CompositorPtr cp = Ogre::CompositorManager::getSingleton(). + create("Rtt_Comp", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + + //output pass + Ogre::CompositionTargetPass* ctp = cp->createTechnique()->getOutputTargetPass(); + Ogre::CompositionPass* cpass = ctp->createPass(); + cpass->setType(Ogre::CompositionPass::PT_RENDERQUAD); + cpass->setMaterial(mp); + + //create a texture to write the texture to... + Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton(). + createManual( + name, + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, + texSize, + texSize, + 0, + tt, + Ogre::TU_RENDERTARGET + ); + + Ogre::RenderTexture* renderTexture = texture->getBuffer()->getRenderTarget(); + Ogre::Viewport* vp = renderTexture->addViewport(mCamera); + + Ogre::CompositorManager::getSingleton().addCompositor(vp, "Rtt_Comp"); + Ogre::CompositorManager::getSingleton().setCompositorEnabled(vp,"Rtt_Comp", true); + + renderTexture->update(); + + // Call the OGRE renderer. + Ogre::Root::getSingleton().renderOneFrame(); + + Ogre::CompositorManager::getSingleton().removeCompositor(vp, "Rtt_Comp"); + Ogre::CompositorManager::getSingleton().remove(cp->getHandle()); + + renderTexture->removeAllViewports(); + + return texture; +} +*/ + +// Create the mesh for this level, by merging the meshes from the +// previous levels. +void mergeMesh(GenLevelResult *sub, ref GenLevelResult res) +{ + // How much to shift various numbers to the left at this level + // (ie. multiply by 2^shift). The height at each vertex is + // multiplied by 8 in each cell to get the final value. However, + // when we're merging two cells (in each direction), we have to + // scale down all the height values by 2 in order to fit the result + // in one byte. This means multiplying the factor by 2 for each + // level above the cell level. + int shift = res.quad.info.level - 1; + assert(shift >= 1); + + // Constants + int intervals = 64; + int vertNum = intervals+1; + int vertSep = 128 << shift; + + // Allocate the result buffer + res.allocMesh(vertNum); + + MeshHolder *mh = &res.quad.meshes[0]; + MeshInfo *mi = &mh.info; + + mi.worldWidth = vertSep*intervals; + assert(mi.worldWidth == 8192<>= 1; + assert(data < 128 && data >= -128); + + *vertPtr++ = data; + + // Copy over the normal + *vertPtr++ = *inPtr++; + *vertPtr++ = *inPtr++; + *vertPtr++ = *inPtr++; + } + // Store the last one here. It _should_ be the + // same as the first in the next section, if + // present. + } + else + { + // No data in this mesh. Just write zeros. + for(int v=0; v<32; v++) + { + // Height + *vertPtr++ = 0; + + // Normal, pointing straight upwards + *vertPtr++ = 0; + *vertPtr++ = 0; + *vertPtr++ = 0x7f; + } + } + } + } + } + assert(vertPtr == mh.vertexBuffer + mi.vertBufSize); + + // Set max and min values here } -void fail(char[] msg) +struct GenLevelResult { - throw new Exception(msg); + bool isAlpha; + + QuadHolder quad; + //PixelBox image; + bool hasMesh; + + void kill() + { + if(!isEmpty()) + { + // This takes care of both normal image data and alpha maps. + free(image.data); + + if(hasMesh) + free(quad.meshes[0].vertexBuffer); + } + } + + ubyte *allocAlphas(int width, int texNum) + { + assert(isEmpty() || hasMesh); + //image = PixelBox(width, width, texNum, Ogre::PF_A8); + + image.data = calloc(width*width*texNum, 1); + isAlpha = true; + + // Set up the alpha images. TODO: We have to split these over + // several meshes, but for now pretend that we're using only + // one. This is going to be a bit messy... Perhaps having only + // separate buffers is good enough, without using the pixel box at + // all. + assert(quad.meshes.size() == 1); + quad.meshes[0].alphas.resize(texNum); + + for(int i=0;i0;rows--) + { + memcpy(to, from, rowSize); + to += tskip; + from += fskip; + } } -extern(C): -void terr_setCacheDir(char *dir); +// ------- OLD CODE - use these snippets later ------- + +// About segments: +/* NOTES for the gen-phase: Was: +// This is pretty messy. Btw: 128*16 == 2048 == +// CELL_WIDTH/4 +// 65 points across one cell means 64 intervals, and 17 points + +// means 16=64/4 intervals. So IOW the number of verts when +// dividing by D is (65-1)/D + 1 = 64/D+1, which means that D +// should divide 64, that is, be a power of two < 64. + +addNewObject(Ogre::Vector3(x*16*128, 0, y*16*128), //pos +17, //size +false, //skirts +0.25f, float(x)/4.0f, float(y)/4.0f);//quad seg location +*/ + +/* This was also declared in the original code, you'll need it + when creating the cache data + + size_t vw = mWidth; // mWidth is 17 or 65 + if ( mUseSkirts ) vw += 2; // skirts are used for level 2 and up + vertCount=vw*vw; +*/ + +/** + * @brief fills the vertex buffer with data + * @todo I don't think tex co-ords are right + void calculateVertexValues() + { + int start = 0; + int end = mWidth; + + if ( mUseSkirts ) + { + --start; + ++end; + } + + for ( int y = start; y < end; y++ ) + for ( int x = start; x < end; x++ ) + { + if ( y < 0 || y > (mWidth-1) || x < 0 || x > (mWidth-1) ) + { + // These are the skirt vertices. 'Skirts' are simply a + // wall at the edges of the mesh that goes straight down, + // cutting off the posibility that you might see 'gaps' + // between the meshes. Or at least I think that's the + // intention. + + assert(mUseSkirts); + + // 1st coordinate + if ( x < 0 ) + *verts++ = 0; + else if ( x > (mWidth-1) ) + *verts++ = (mWidth-1)*getVertexSeperation(); + else + *verts++ = x*getVertexSeperation(); + + // 2nd coordinate + *verts++ = -4096; //2048 below base sea floor height + + // 3rd coordinate + if ( y < 0 ) + *verts++ = 0; + else if ( y > (mWidth-1) ) + *verts++ = (mWidth-1)*getVertexSeperation(); + else + *verts++ = y*getVertexSeperation(); + + // No normals + for ( Ogre::uint i = 0; i < 3; i++ ) + *verts++ = 0; + + // It shouldn't matter if these go over 1 + float u = (float)(x) / (mWidth-1); + float v = (float)(y) / (mWidth-1); + *verts++ = u; + *verts++ = v; + } + else // Covered already + + void calculateIndexValues() + { + size_t vw = mWidth-1; + if ( mUseSkirts ) vw += 2; + + const size_t indexCount = (vw)*(vw)*6; + + //need to manage allocation if not null + assert(mIndices==0); + + // buffer was created here + + bool flag = false; + Ogre::uint indNum = 0; + for ( Ogre::uint y = 0; y < (vw); y+=1 ) { + for ( Ogre::uint x = 0; x < (vw); x+=1 ) { + + const int line1 = y * (vw+1) + x; + const int line2 = (y + 1) * (vw+1) + x; + + if ( flag ) { + *indices++ = line1; + *indices++ = line2; + *indices++ = line1 + 1; + + *indices++ = line1 + 1; + *indices++ = line2; + *indices++ = line2 + 1; + } else { + *indices++ = line1; + *indices++ = line2; + *indices++ = line2 + 1; + + *indices++ = line1; + *indices++ = line2 + 1; + *indices++ = line1 + 1; + } + flag = !flag; //flip tris for next time + + indNum+=6; + } + flag = !flag; //flip tries for next row + } + assert(indNum==indexCount); + //return mIndices; + } +*/ ++/ diff --git a/terrain/terrain.d b/terrain/terrain.d index 159a01f07..ffc6f74d5 100644 --- a/terrain/terrain.d +++ b/terrain/terrain.d @@ -27,8 +27,10 @@ import terrain.generator; void initTerrain(bool doGen) { + /* if(doGen) generate(); + */ //terr_setupRendering(); } diff --git a/util/cachefile.d b/util/cachefile.d new file mode 100644 index 000000000..ef5cbfbf9 --- /dev/null +++ b/util/cachefile.d @@ -0,0 +1,48 @@ +/* + OpenMW - The completely unofficial reimplementation of Morrowind + Copyright (C) 2008-2009 Nicolay Korslund + Email: < korslund@gmail.com > + WWW: http://openmw.sourceforge.net/ + + This file (cachefile.d) is part of the OpenMW package. + + OpenMW is distributed as free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License + version 3, as published by the Free Software Foundation. + + 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 + version 3 along with this program. If not, see + http://www.gnu.org/licenses/ . + +*/ + +module util.cachefile; +import monster.util.string; +import std.file; + +void makeDir(char[] pt) +{ + if(exists(pt)) + { + if(!isdir(pt)) + throw new Exception(pt ~ " is not a directory"); + } + else + mkdir(pt); +} + +void makePath(char[] pt) +{ + assert(!pt.begins("/")); + foreach(int i, char c; pt) + if(c == '/') + makeDir(pt[0..i]); + + if(!pt.ends("/")) + makeDir(pt); +}