Exemplo n.º 1
0
bool TSTerrainSceneNode::loadHeightMap(
   const c8* name,
   video::IImage* heightmap,
   const core::dimension2d<f32>& stretchSize,
   f32 maxHeight, const core::dimension2d<s32> maxVtxBlockSize,
   bool debugBorders)
{
   bool result = false;

   if(name != NULL && strlen(name) > 0 &&
      heightmap != NULL && this->my_timer != NULL && this->my_videoDriver != NULL)
   {
      u32 startTime = my_timer->getRealTime();

      video::SMaterial material;

      c8 stringBuffer[256];
      c8 textureName[64];

      s32 borderSkip = debugBorders ? 0 : 1;

      video::S3DVertex2TCoords vtx;
      vtx.Color.set(255,255,255,255);

      scene::SMesh* mesh   = new scene::SMesh();

      u32 tm                     = this->my_timer->getRealTime() / 1000;
      core::dimension2d<s32> hMapSize   = heightmap->getDimension();
      core::dimension2d<s32> tMapSize = heightmap->getDimension();
      core::position2d<f32> thRel((f32)tMapSize.Width / hMapSize.Width, (f32)tMapSize.Height / hMapSize.Height);
      core::position2d<s32> processed(0,0);


#ifdef _USING_SMOOTH_FACTOR

      s32 widthInBlock         = 0;
      s32 heightInBlock         = 0;
      bool noMoreWidthCounting   = false;

      scene::SMeshBufferLightMap *wholeTerrain   = new scene::SMeshBufferLightMap();
      wholeTerrain->Vertices.set_used(hMapSize.Height * hMapSize.Width);
      u32 wholeTerrainIndex                  = 0;

#endif

      while(processed.Y < hMapSize.Height)
      {
#ifdef _USING_SMOOTH_FACTOR
         heightInBlock++;
#endif

         while(processed.X < hMapSize.Width)
         {
#ifdef _USING_SMOOTH_FACTOR
            if(!noMoreWidthCounting)
               widthInBlock++;
#endif

            // 1. Calculate the block size
            core::dimension2d<s32> blockSize = maxVtxBlockSize;

            if (processed.X + blockSize.Width   > hMapSize.Width)
               blockSize.Width      = hMapSize.Width - processed.X;
            if (processed.Y + blockSize.Height   > hMapSize.Height)
               blockSize.Height   = hMapSize.Height - processed.Y;

            // DC NOTE: For Applying Texture Splatting.
            //         typedef CMeshBuffer<video::S3DVertex2TCoords> SMeshBufferLightMap
            scene::SMeshBufferLightMap* buffer = new scene::SMeshBufferLightMap();

            // 2. Add vertices of vertex block
            s32 y;

            const f32 resDTBySize   = this->my_detailTextScale   /   (f32)(hMapSize.Width - 1);
            const f32 resLMBySize   = this->my_lightMapTextScale/   (f32)(hMapSize.Width - 1);
            const f32 resCMBySize   = this->my_colorMapTextScale/   (f32)(hMapSize.Width - 1);
            const f32 res1BySize   = this->my_text1Scale      /   (f32)(hMapSize.Width - 1);
            const f32 res2BySize   = this->my_text2Scale      /   (f32)(hMapSize.Width - 1);
            const f32 res3BySize   = this->my_text3Scale      /   (f32)(hMapSize.Width - 1);
            const f32 res4BySize   = this->my_text4Scale      /   (f32)(hMapSize.Width - 1);

            f32 xDTval   = 0, zDTval   = 0;
            f32 xLMval   = 0, zLMval = 0;
            f32 xCMval   = 0, zCMval   = 0;
            f32 x1val   = 0, z1val   = 0;
            f32 x2val   = 0, z2val   = 0;
            f32 x3val   = 0, z3val   = 0;
            f32 x4val   = 0, z4val   = 0;


            for (y=0; y < blockSize.Height; ++y)
            {
               if(zDTval == 0)
               {
                  zDTval   =   resDTBySize   * (y + processed.Y);
                  zLMval   =   resLMBySize   * (y + processed.Y);
                  zCMval   =   resCMBySize   * (y + processed.Y);
                  z1val   =   res1BySize   * (y + processed.Y);
                  z2val   =   res2BySize   * (y + processed.Y);
                  z3val   =   res3BySize   * (y + processed.Y);
                  z4val   =   res4BySize   * (y + processed.Y);
               }

               for (s32 x=0; x < blockSize.Width; ++x)
               {
                  if(xDTval == 0)
                  {
                     xDTval   +=   resDTBySize   * (x + processed.X);
                     xLMval   +=   resLMBySize   * (x + processed.X);
                     xCMval   +=   resCMBySize   * (x + processed.X);
                     x1val   +=   res1BySize   * (x + processed.X);
                     x2val   +=   res2BySize   * (x + processed.X);
                     x3val   +=   res3BySize   * (x + processed.X);
                     x4val   +=   res4BySize   * (x + processed.X);
                  }

                  video::SColor clr = heightmap->getPixel(x + processed.X, y + processed.Y);
                  f32 height = ((clr.getRed() + clr.getGreen() + clr.getBlue()) / 3.0f) / 255.0f * maxHeight;

                  vtx.Pos.set((f32)(x + processed.X) * stretchSize.Width,
                     height, (f32)(y + processed.Y) * stretchSize.Height);

                  vtx.TCoords.set((x + 0.5f) / blockSize.Width,
                     (y + 0.5f) / blockSize.Height);

                  vtx.TCoords.X   = xDTval;
                  vtx.TCoords6.X   = xLMval;
                  vtx.TCoords7.X   = xCMval;
                  vtx.TCoords.Y   = zDTval;
                  vtx.TCoords6.Y   = zLMval;
                  vtx.TCoords7.Y   = zCMval;

                  if(this->my_text1Scale == 0)
                     vtx.TCoords2   = vtx.TCoords;
                  else
                  {
                     vtx.TCoords2.X   = x1val;
                     vtx.TCoords2.Y   = z1val;
                  }

                  if(this->my_text2Scale == 0)
                     vtx.TCoords3   = vtx.TCoords;
                  else
                  {
                     vtx.TCoords3.X   = x2val;
                     vtx.TCoords3.Y   = z2val;
                  }

                  if(this->my_text3Scale == 0)
                     vtx.TCoords4   = vtx.TCoords;
                  else
                  {
                     vtx.TCoords4.X   = x3val;
                     vtx.TCoords4.Y   = z3val;
                  }

                  if(this->my_text4Scale == 0)
                     vtx.TCoords5   = vtx.TCoords;
                  else
                  {
                     vtx.TCoords5.X   = x4val;
                     vtx.TCoords5.Y   = z4val;
                  }

                  buffer->Vertices.push_back(vtx);

#ifdef _USING_SMOOTH_FACTOR
                  wholeTerrainIndex = (x + processed.X) * hMapSize.Width + (y + processed.Y);

                  wholeTerrain->Vertices[wholeTerrainIndex] = vtx;
#endif


                  xDTval   +=   resDTBySize;
                  xLMval   +=   resLMBySize;
                  xCMval   +=   resCMBySize;
                  x1val   +=   res1BySize;
                  x2val   +=   res2BySize;
                  x3val   +=   res3BySize;
                  x4val   +=   res4BySize;
               }

               xDTval = xLMval = xCMval = x1val = x2val = x3val = x4val = 0;

               zDTval   +=   resDTBySize;
               zLMval   +=   resLMBySize;
               zCMval   +=   resCMBySize;
               z1val   +=   res1BySize;
               z2val   +=   res2BySize;
               z3val   +=   res3BySize;
               z4val   +=   res4BySize;
            }

            // 3. Add indices of vertex block
            for(y = 0; y < blockSize.Height - 1; ++y)
            {
               for (s32 x = 0; x < blockSize.Width - 1; ++x)
               {
                  s32 c = (y * blockSize.Width) + x;

                  buffer->Indices.push_back(c);
                  buffer->Indices.push_back(c + blockSize.Width);
                  buffer->Indices.push_back(c + 1);

                  buffer->Indices.push_back(c + 1);
                  buffer->Indices.push_back(c + blockSize.Width);
                  buffer->Indices.push_back(c + 1 + blockSize.Width);
               }
            }

            // 4. Recalculate normals
            for(s32 i = 0; i < (s32)buffer->Indices.size(); i += 3)
            {
               core::plane3d<f32> p(
                  buffer->Vertices[buffer->Indices[i + 0]].Pos,
                  buffer->Vertices[buffer->Indices[i + 1]].Pos,
                  buffer->Vertices[buffer->Indices[i + 2]].Pos);
               p.Normal.normalize();

               buffer->Vertices[buffer->Indices[i + 0]].Normal = p.Normal;
               buffer->Vertices[buffer->Indices[i + 1]].Normal = p.Normal;
               buffer->Vertices[buffer->Indices[i + 2]].Normal = p.Normal;
            }

            if (buffer->Vertices.size())
            {
				//Notice: gia ma de compile thanh cong, doan comment o duoi moi la code chinh
               // create texture for this block
				video::IImage* img = this->my_videoDriver->createImage( video::ECOLOR_FORMAT::ECF_A1R5G5B5, 
					core::dimension2d<s32>((s32)(blockSize.Width * thRel.X), (s32)(blockSize.Height * thRel.Y)));

               /*video::IImage* img = this->my_videoDriver->createImage(
				  heightmap,
                  core::position2d<s32>((s32)(processed.X * thRel.X), (s32)(processed.Y * thRel.Y)),
                  core::dimension2d<s32>((s32)(blockSize.Width * thRel.X), (s32)(blockSize.Height * thRel.Y)));*/

               sprintf_s(textureName, 64, "terrain%u_%d", tm, mesh->getMeshBufferCount());
			   
			   material.setTexture(0, this->my_videoDriver->addTexture(textureName, img));

               if (material.getTexture(0))
               {
                  sprintf_s(stringBuffer, 256, "Generated terrain texture (%dx%d): %s\n",
                     material.getTexture(0)->getSize().Width,
                     material.getTexture(0)->getSize().Height,
                     textureName);
                  this->my_logger->log(stringBuffer);
               }
               else
                  printf("Could not create terrain texture.", textureName);

               buffer->Material = material;
               img->drop();
            }

            buffer->recalculateBoundingBox();
            mesh->addMeshBuffer(buffer);
            buffer->drop();

            processed.X += maxVtxBlockSize.Width - borderSkip;

         } // while(processed.X<hMapSize.Width)

#ifdef _USING_SMOOTH_FACTOR
         noMoreWidthCounting = true;
#endif

         // keep on processing
         processed.X = 0;
         processed.Y += maxVtxBlockSize.Height - borderSkip;
      } // while (processed.Y<hMapSize.Height)

      scene::SAnimatedMesh* animatedMesh = new scene::SAnimatedMesh();
      mesh->recalculateBoundingBox();
      this->my_mesh = mesh;


#ifdef _USING_SMOOTH_FACTOR

      for( s32 i=0; i < 5 ;i++)
      {
         for( s32 x=0; x < hMapSize.Width ; x++)
         {
            for( s32 z=0; z < hMapSize.Height ; z++)
            {
               int counter=1;

               if( x>0 && z > 0){
                 wholeTerrain->Vertices[x + z * hMapSize.Height].Pos.Y += wholeTerrain->Vertices[(x-1) + (z-1) * hMapSize.Height].Pos.Y;
                 counter++;
               }
               if( x>0 ){
                 wholeTerrain->Vertices[x + z * hMapSize.Height].Pos.Y += wholeTerrain->Vertices[(x-1) + (z) * hMapSize.Height].Pos.Y;
                 counter++;
               }
               if( x>0 && z < hMapSize.Height-1){
                 wholeTerrain->Vertices[x + z * hMapSize.Height].Pos.Y += wholeTerrain->Vertices[(x-1) + (z+1) * hMapSize.Height].Pos.Y;
                 counter++;
               }
               if( x < hMapSize.Width-1 && z > 0){
                 wholeTerrain->Vertices[x + z * hMapSize.Height].Pos.Y += wholeTerrain->Vertices[(x+1) + (z-1) * hMapSize.Height].Pos.Y;
                 counter++;
               }
               if( x < hMapSize.Width-1 ){
                 wholeTerrain->Vertices[x + z * hMapSize.Height].Pos.Y += wholeTerrain->Vertices[(x+1) + (z) * hMapSize.Height].Pos.Y;
                 counter++;
               }
               if( x < hMapSize.Width-1 && z < hMapSize.Height-1){
                 wholeTerrain->Vertices[x + z * hMapSize.Height].Pos.Y += wholeTerrain->Vertices[(x+1) + (z+1) * hMapSize.Height].Pos.Y;
                 counter++;
               }
               if( z > 0 ){
                 wholeTerrain->Vertices[x + z * hMapSize.Height].Pos.Y += wholeTerrain->Vertices[(x) + (z-1) * hMapSize.Height].Pos.Y;
                 counter++;
               }
               if( z < hMapSize.Height-1 ){
                 wholeTerrain->Vertices[x + z * hMapSize.Height].Pos.Y += wholeTerrain->Vertices[(x) + (z+1) * hMapSize.Height].Pos.Y;
                 counter++;
               }
               wholeTerrain->Vertices[x + z * hMapSize.Height].Pos.Y /= (float)counter;
            }
         }
      }

      this->calculateNormals(wholeTerrain, core::dimension2d<s32>(hMapSize.Width, hMapSize.Height));

      // Convert to the Blocks
      s32 curBlockIndex         = 0;
      s32 vertexIndex            = 0;

      processed.X = processed.Y   = 0;

      while(processed.Y < hMapSize.Height)
      {
         while(processed.X < hMapSize.Width)
         {

            // 1. Calculate the block size
            core::dimension2d<s32> blockSize = maxVtxBlockSize;

            if (processed.X + blockSize.Width   > hMapSize.Width)
               blockSize.Width      = hMapSize.Width - processed.X;
            if (processed.Y + blockSize.Height   > hMapSize.Height)
               blockSize.Height   = hMapSize.Height - processed.Y;

            vertexIndex = 0;

            s32 y;

            for (y=0; y < blockSize.Height; ++y)
            {
               for (s32 x=0; x < blockSize.Width; ++x)
               {
                  wholeTerrainIndex = (x + processed.X) * hMapSize.Width + (y + processed.Y);

                  ((scene::SMeshBufferLightMap *)my_mesh->getMeshBuffer(curBlockIndex))->Vertices[vertexIndex].Pos
                     = wholeTerrain->Vertices[wholeTerrainIndex].Pos;

                  vertexIndex++;
               }
            }

/*
            // 4. Recalculate normals
            for(s32 i = 0; i < (s32)buffer->Indices.size(); i += 3)
            {
               core::plane3d<f32> p(
                  buffer->Vertices[buffer->Indices[i + 0]].Pos,
                  buffer->Vertices[buffer->Indices[i + 1]].Pos,
                  buffer->Vertices[buffer->Indices[i + 2]].Pos);
               p.Normal.normalize();

               buffer->Vertices[buffer->Indices[i + 0]].Normal = p.Normal;
               buffer->Vertices[buffer->Indices[i + 1]].Normal = p.Normal;
               buffer->Vertices[buffer->Indices[i + 2]].Normal = p.Normal;
            }
*/
            curBlockIndex++;
               
            processed.X += maxVtxBlockSize.Width - borderSkip;

         } // while(processed.X<hMapSize.Width)

         // keep on processing
         processed.X = 0;
         processed.Y += maxVtxBlockSize.Height - borderSkip;
      } // while (processed.Y<hMapSize.Height)

      wholeTerrain->drop();
      wholeTerrain = NULL;

#endif


      video::SMaterial mat;
      for (u32 i = 0; i < this->my_mesh->getMeshBufferCount(); ++i)
      {
         scene::IMeshBuffer* mb = this->my_mesh->getMeshBuffer(i);
         if (mb)
            mat = mb->getMaterial();

         this->my_materials.push_back(mat);
      }

      u32 endTime = this->my_timer->getRealTime();

      sprintf_s(stringBuffer, 256, "Generated terrain data (%dx%d) in %.4f seconds\n",
         hMapSize.Width, hMapSize.Height, (endTime - startTime) / 1000.0f);

      this->my_logger->log(stringBuffer);

      if(this->my_mesh != NULL)
      {
         this->my_mesh->grab();
         result = true;
      }
   }

   return result;
}
IAnimatedMesh* CGeometryCreator::createTerrainMesh(video::IImage* texture,
        video::IImage* heightmap, const core::dimension2d<f32>& stretchSize,
        f32 maxHeight, video::IVideoDriver* driver,
        const core::dimension2d<s32> maxVtxBlockSize,
        bool debugBorders)
{
    u32 tm = os::Timer::getRealTime()/1000;

    if (!texture || !heightmap)
        return 0;

    video::SMaterial material;
    c8 textureName[64];
    c8 tmp[255];


    // debug border
    s32 borderSkip = debugBorders ? 0 : 1;

    video::S3DVertex vtx;
    vtx.Color.set(255,255,255,255);

    SMesh* mesh = new SMesh();

    core::dimension2d<s32> hMapSize= heightmap->getDimension();
    core::dimension2d<s32> tMapSize= texture->getDimension();
    core::position2d<f32> thRel((f32)tMapSize.Width / (s32)hMapSize.Width, (f32)tMapSize.Height / (s32)hMapSize.Height);
    core::position2d<s32> processed(0,0);

    while (processed.Y<hMapSize.Height)
    {
        while(processed.X<hMapSize.Width)
        {
            core::dimension2d<s32> blockSize = maxVtxBlockSize;
            if (processed.X + blockSize.Width > hMapSize.Width)
                blockSize.Width = hMapSize.Width - processed.X;
            if (processed.Y + blockSize.Height > hMapSize.Height)
                blockSize.Height = hMapSize.Height - processed.Y;

            SMeshBuffer* buffer = new SMeshBuffer();
            s32 x,y;

            // add vertices of vertex block
            for (y=0; y<blockSize.Height; ++y)
                for (x=0; x<blockSize.Width; ++x)
                {
                    video::SColor clr = heightmap->getPixel(x+processed.X, y+processed.Y);
                    f32 height = ((clr.getRed() + clr.getGreen() + clr.getBlue()) / 3.0f)/255.0f * maxHeight;

                    vtx.Pos.set((f32)(x+processed.X) * stretchSize.Width,
                                height, (f32)(y+processed.Y) * stretchSize.Height);

                    vtx.TCoords.set((f32)(x+0.5f) / ((f32)blockSize.Width),
                                    (f32)(y+0.5f) / ((f32)blockSize.Height));

                    buffer->Vertices.push_back(vtx);
                }

            // add indices of vertex block
            for (y=0; y<blockSize.Height-1; ++y)
                for (x=0; x<blockSize.Width-1; ++x)
                {
                    s32 c = (y*blockSize.Width) + x;

                    buffer->Indices.push_back(c);
                    buffer->Indices.push_back(c + blockSize.Width);
                    buffer->Indices.push_back(c + 1);

                    buffer->Indices.push_back(c + 1);
                    buffer->Indices.push_back(c + blockSize.Width);
                    buffer->Indices.push_back(c + 1 + blockSize.Width);
                }

            // recalculate normals
            for (s32 i=0; i<(s32)buffer->Indices.size(); i+=3)
            {
                core::plane3d<f32> p(
                    buffer->Vertices[buffer->Indices[i+0]].Pos,
                    buffer->Vertices[buffer->Indices[i+1]].Pos,
                    buffer->Vertices[buffer->Indices[i+2]].Pos);
                p.Normal.normalize();

                buffer->Vertices[buffer->Indices[i+0]].Normal = p.Normal;
                buffer->Vertices[buffer->Indices[i+1]].Normal = p.Normal;
                buffer->Vertices[buffer->Indices[i+2]].Normal = p.Normal;
            }

            if (buffer->Vertices.size())
            {
                // create texture for this block
                video::IImage* img = new video::CImage(texture,
                                                       core::position2d<s32>((s32)(processed.X*thRel.X), (s32)(processed.Y*thRel.Y)),
                                                       core::dimension2d<s32>((s32)(blockSize.Width*thRel.X), (s32)(blockSize.Height*thRel.Y)));

                sprintf(textureName, "terrain%d_%d", tm, mesh->getMeshBufferCount());

                material.Texture1 = driver->addTexture(textureName, img);

                if (material.Texture1)
                {
                    sprintf(tmp, "Generated terrain texture (%dx%d): %s",
                            material.Texture1->getSize().Width,
                            material.Texture1->getSize().Height,
                            textureName);
                    os::Printer::log(tmp);
                }
                else
                    os::Printer::log("Could not create terrain texture.", textureName, ELL_ERROR);

                buffer->Material = material;
                img->drop();
            }

            buffer->recalculateBoundingBox();
            mesh->addMeshBuffer(buffer);
            buffer->drop();

            // keep on processing
            processed.X += maxVtxBlockSize.Width - borderSkip;
        }

        // keep on processing
        processed.X = 0;
        processed.Y += maxVtxBlockSize.Height - borderSkip;
    }

    SAnimatedMesh* animatedMesh = new SAnimatedMesh();
    mesh->recalculateBoundingBox();
    animatedMesh->addMesh(mesh);
    animatedMesh->recalculateBoundingBox();

    mesh->drop();

    return animatedMesh;
}