Sunday, December 7, 2014

Ogre Perlin Noise Algorithm implementation

 #include <vector>  

 using namespace std;
 class Perlin{
      public:
           Perlin(void);
           Perlin(int gridsizex, int gridsizey);
           Perlin(int gridsizex, int gridsizey, int gridsizez);
           float getNoiseValue(float x, float y);
           void dnoise3f( vector<float>* vout, float x, float y, float z );
           float getNoiseValue(float x, float y, float z);
      private:
           float dotGridGradient(int ix, int iy, float x, float y);
           float dotGridGradient(int ix, int iy, int iz, float x, float y, float z);
           vector<vector<Ogre::Vector2> > Gradient;
           vector<vector<vector<Ogre::Vector3> > > Gradient3d;
           float lerp(float a0, float a1, float w);
           Ogre::Vector2 randomPointUnitCircle(void);
           Ogre::Vector3 randomPointUnitSphere(void);
           float myRandomMagic(float x, float y, float z, int ix, int iy, int iz);
           void iGetIntegerAndFractional(float val, int* outi, float* outd);          
 };
 Perlin::Perlin(){
 }
 Perlin::Perlin(int gridsizex, int gridsizey){
      Gradient.resize(gridsizex);
      for (int i = 0; i < gridsizex; i++){
           Gradient[i].resize(gridsizey);
           for(int j = 0; j < gridsizey; j++){
                Gradient[i][j] = randomPointUnitCircle();
           }
      }
 }
 Perlin::Perlin(int gridsizex, int gridsizey, int gridsizez){
      Gradient3d.resize(gridsizex);
      for (int i = 0; i < gridsizex; i++){
           Gradient3d[i].resize(gridsizey);
           for(int j = 0; j < gridsizey; j++){
                Gradient3d[i][j].resize(gridsizez);
                for (int k = 0; k < gridsizez; k++){
                     Gradient3d[i][j][k] = randomPointUnitSphere();
                }
                //Gradient[i][j] = randomPointUnitCircle();
           }
      }
 }
 Ogre::Vector2 Perlin::randomPointUnitCircle(){
      int v1 = rand() % 360;     // v1 in the range 0 to 360 degrees
      Ogre::Real y = Ogre::Math::Sin(Ogre::Math::DegreesToRadians(v1));
      Ogre::Real x = Ogre::Math::Cos(Ogre::Math::DegreesToRadians(v1));
      return Ogre::Vector2(x,y);    
 }
 Ogre::Vector3 Perlin::randomPointUnitSphere(){
      int theta = rand() % 359;
      int phi = rand() % 359;
      //Generated from Spherical Coordinates
      Ogre::Real y = Ogre::Math::Sin(Ogre::Math::DegreesToRadians(theta))*Ogre::Math::Sin(Ogre::Math::DegreesToRadians(phi));
      Ogre::Real x = Ogre::Math::Cos(Ogre::Math::DegreesToRadians(theta))*Ogre::Math::Sin(Ogre::Math::DegreesToRadians(phi));
      Ogre::Real z = Ogre::Math::Cos(Ogre::Math::DegreesToRadians(phi));
      return Ogre::Vector3(x,y,z);
 }
  // Function to linearly interpolate between a0 and a1
  // Weight w should be in the range [0.0, 1.0]
  float Perlin::lerp(float a0, float a1, float w) {
    return (1.0 - w)*a0 + w*a1;
  }
  // Computes the dot product of the distance and gradient vectors.
  float Perlin::dotGridGradient(int ix, int iy, float x, float y) {
    // Precomputed (or otherwise) gradient vectors at each grid point X,Y
    //extern float Gradient[Y][X][2];
    // Compute the distance vector
    float dx = x - (double)ix;
    float dy = y - (double)iy;
    // Compute the dot-product
    return (dx*Gradient[ix][iy][0] + dy*Gradient[ix][iy][1]);
  }
  // Computes the dot product of the distance and gradient vectors.
  float Perlin::dotGridGradient(int ix, int iy, int iz, float x, float y, float z) {
    // Precomputed (or otherwise) gradient vectors at each grid point X,Y
    //extern float Gradient[Y][X][2];
    // Compute the distance vector
    float dx = x - (double)ix;
    float dy = y - (double)iy;
    float dz = z - (double)iz;
    // Compute the dot-product
    return (dx*(float)Gradient3d[ix][iy][iz][0] + dy*(float)Gradient3d[ix][iy][iz][1] +dz*(float)Gradient3d[ix][iy][iz][2]);
  }
  // Compute Perlin noise at coordinates x, y
  float Perlin::getNoiseValue(float x, float y) {
    //Ogre::Log* tlog = Ogre::LogManager::getSingleton().getLog("Perlin.log");
    // Determine grid cell coordinates
    int x0 = (x > 0.0 ? (int)x : (int)x );
    int x1 = x0 + 1;
    int y0 = (y > 0.0 ? (int)y : (int)y );
    int y1 = y0 + 1;
    // Determine interpolation weights
    // Could also use higher order polynomial/s-curve here
    float sx = x - (double)x0;
    float sy = y - (double)y0;
    std::ostringstream ss5;
    //ss5<< "Test4" << "\n";
    //ss5<< x0<<","<<y0<< ","<< x<< ","<< y<<"\n";
    //tlog->logMessage(ss5.str());
    //ss5.str(std::string());
    //ss5<<"Gradient: " <<Gradient[x0][y0]<<"\n";
    //tlog->logMessage(ss5.str());
    // Interpolate between grid point gradients
    float n0, n1, ix0, ix1, value;
    n0 = dotGridGradient(x0, y0, x, y);
    n1 = dotGridGradient(x1, y0, x, y);
    //ss5.str(std::string());
    //ss5<< "Test5" << "\n";
    ix0 = lerp(n0, n1, sx);
    //ss5.str(std::string());
    //ss5<< "Test5" << "\n";
    //tlog->logMessage(ss5.str());
    n0 = dotGridGradient(x0, y1, x, y);
    n1 = dotGridGradient(x1, y1, x, y);
    ix1 = lerp(n0, n1, sx);
    value = lerp(ix0, ix1, sy);
    return value;
  }
  float Perlin::getNoiseValue(float x, float y, float z) {
    //Ogre::Log* tlog = Ogre::LogManager::getSingleton().getLog("Perlin.log");
    // Determine grid cell coordinates
 /*
 On a periodic and cubic lattice, let x_d, y_d, and z_d be the differences between each of x, y, z and the smaller coordinate related, that is:
  \ x_d = (x - x_0)/(x_1 - x_0)
  \ y_d = (y - y_0)/(y_1 - y_0)
  \ z_d = (z - z_0)/(z_1 - z_0)
 where x_0 indicates the lattice point below x , and  x_1 indicates the lattice point above x and similarly for y_0, y_1, z_0 and z_1.
 First we interpolate along x (imagine we are pushing the front face of the cube to the back), giving:
  \ c_{00} = V[x_0,y_0, z_0] (1 - x_d) + V[x_1, y_0, z_0] x_d
  \ c_{10} = V[x_0,y_1, z_0] (1 - x_d) + V[x_1, y_1, z_0] x_d
  \ c_{01} = V[x_0,y_0, z_1] (1 - x_d) + V[x_1, y_0, z_1] x_d
  \ c_{11} = V[x_0,y_1, z_1] (1 - x_d) + V[x_1, y_1, z_1] x_d
 Where V[x_0,y_0, z_0] means the function value of (x_0,y_0,z_0). Then we interpolate these values (along y, as we were pushing the top edge to the bottom), giving:
  \ c_0 = c_{00}(1 - y_d) + c_{10}y_d
  \ c_1 = c_{01}(1 - y_d) + c_{11}y_d
 Finally we interpolate these values along z(walking through a line):
  \ c = c_0(1 - z_d) + c_1z_d .
 */
    int x0 = (x > 0.0 ? (int)x : (int)x );
    int x1 = x0 + 1;
    int y0 = (y > 0.0 ? (int)y : (int)y );
    int y1 = y0 + 1;
    int z0 = (int)z;
    int z1 = z0 + 1;
    // Determine interpolation weights
    // Could also use higher order polynomial/s-curve here
    float sx = x - (double)x0;
    float sy = y - (double)y0;
    float sz = z - (double)z0;
    float u = 6.0f*pow(sx,5) - 15*pow(sx,4)+10*pow(sx,3);
    float v = 6.0f*pow(sy,5) - 15*pow(sy,4)+10*pow(sy,3);
    float w = 6.0f*pow(sz,5) - 15*pow(sz,4)+10*pow(sz,3);
    std::ostringstream ss5;
    //ss5<< "Test4" << "\n";
    //ss5<< x0<<","<<y0<< ","<< x<< ","<< y<<"\n";
    //tlog->logMessage(ss5.str());
    //ss5.str(std::string());
    //ss5<<"Gradient: " <<Gradient[x0][y0]<<"\n";
    //tlog->logMessage(ss5.str());
    // Interpolate between grid point gradients
    float n000, n100, n010, n001, n110, n101, n011, n111 ,c00, c10, c01, c11, c0,c1,value;
    n000 = dotGridGradient(x0, y0, z0, x, y, z);
    n100 = dotGridGradient(x1, y0, z0, x, y, z);
    n010 = dotGridGradient(x0, y1, z0, x, y, z);
    n001 = dotGridGradient(x0, y0, z1, x, y, z);
    n110 = dotGridGradient(x1, y1, z0, x, y, z);
    n101 = dotGridGradient(x1, y0, z1, x, y, z);
    n011 = dotGridGradient(x0, y1, z1, x, y, z);
    n111 = dotGridGradient(x1, y1, z1, x, y, z);
    //ss5.str(std::string());
    //ss5<< "Test5" << "\n";
    c00 = lerp(n000, n100, u);
    c10 = lerp(n010, n110, u);
    c01 = lerp(n001, n101, u);
    c11 = lerp(n011, n111, u);
    c0 = lerp(c00, c10, v);
    c1 = lerp(c01, c11, v);
    value = lerp(c0,c1, w);
    //ss5.str(std::string());
    //ss5<< "Test5" << "\n";
    //tlog->logMessage(ss5.str());
    //n0 = dotGridGradient(x0, y1, x, y);
    //n1 = dotGridGradient(x1, y1, x, y);
    //ix1 = lerp(n0, n1, sx);
    //value = lerp(ix0, ix1, sy);
    return value;
  }
 void Perlin::iGetIntegerAndFractional(float val, int* outi, float* outd){
   int outu;
   float outv;
   outu = (int)val;
   outv = (float)(val - (int)val);
   outi = &outu;
   outd = &outv;
 }
 float Perlin::myRandomMagic(float x, float y, float z, int ix, int iy, int iz){
 //     float val = dotGridGradient(ix, iy, iz, x, y, z);
      //float returnval;
      float val = Gradient3d[ix][iy][iz][0];
      return val;
 }
 void Perlin::dnoise3f( vector<float>* vout, float x, float y, float z )
 {
   int  i, j, k;
   float u, v, w;
   iGetIntegerAndFractional( x, &i, &u );
   iGetIntegerAndFractional( y, &j, &v );
   iGetIntegerAndFractional( z, &k, &w );
   const float du = 30.0f*u*u*(u*(u-2.0f)+1.0f);
   const float dv = 30.0f*v*v*(v*(v-2.0f)+1.0f);
   const float dw = 30.0f*w*w*(w*(w-2.0f)+1.0f);
   u = u*u*u*(u*(u*6.0f-15.0f)+10.0f);
   v = v*v*v*(v*(v*6.0f-15.0f)+10.0f);
   w = w*w*w*(w*(w*6.0f-15.0f)+10.0f);
   const float a = myRandomMagic( x,y,z,i+0, j+0, k+0 );
   const float b = myRandomMagic( x,y,z,i+1, j+0, k+0 );
   const float c = myRandomMagic( x,y,z,i+0, j+1, k+0 );
   const float d = myRandomMagic( x,y,z,i+1, j+1, k+0 );
   const float e = myRandomMagic( x,y,z,i+0, j+0, k+1 );
   const float f = myRandomMagic( x,y,z,i+1, j+0, k+1 );
   const float g = myRandomMagic( x,y,z,i+0, j+1, k+1 );
   const float h = myRandomMagic( x,y,z,i+1, j+1, k+1 );
   const float k0 =  a;
   const float k1 =  b - a;
   const float k2 =  c - a;
   const float k3 =  e - a;
   const float k4 =  a - b - c + d;
   const float k5 =  a - c - e + g;
   const float k6 =  a - b - e + f;
   const float k7 = - a + b + c - d + e - f - g + h;
   vector<float> pvout(4);
   pvout[0] = k0 + k1*u + k2*v + k3*w + k4*u*v + k5*v*w + k6*w*u + k7*u*v*w;
   pvout[1] = du * (k1 + k4*v + k6*w + k7*v*w);
   pvout[2] = dv * (k2 + k5*w + k4*u + k7*w*u);
   pvout[3] = dw * (k3 + k6*u + k5*v + k7*u*v);
   vout = &pvout;
 }

Todays and yesterdays work with the Perlin noise generator.

Basically I adapted code from two sites, although it appears the dnoise3f method call is off in implementation.  I instead used a previous algorithm and adapted this for the three dimensional case using tri linear interpolation.  Also a hermite spline quintic polynomial is provisioned for parametric smoothing on the node (floor of the point coordinate) to point in space, as to how and why mathematically speaking this is important can be seen in the differences for implementing the perlin algorithm not using this smoothing function versus using it.  What appears are node artifacts which appear discontinuous from node cell to node cell.

Here's the quintic function:

float u = 6.0f*pow(sx,5) - 15*pow(sx,4)+10*pow(sx,3);

for example.  The reason for smoothness from gradient node cell to gradient node cell relates to derivatives evidently which according to this parametric form provide better weight continuity along the edges of the gradient node boundaries.

Here's an example of a two dimension slice on a three dimensional implementation with the quintic hermite implementation:






















Here's a 2 d example without the quintic smoothing function:





















Artifacts more obviously shown.  You can examine the code above for both the two and three dimension forms to see the differences here in implementation.  

Friday, December 5, 2014

Ogre Terrain Height Selection class

 #include <iostream>  

 #include <string>
 #include <sstream>
 #include <vector>
 class Terrainheightselection{
      public:
           Terrainheightselection(Ogre::TerrainGroup* mTerraingroup, Ogre::Vector3 pos, double selsize);
           std::vector<Ogre::Vector3> getSelectionVerts(void);
      private:
           Ogre::TerrainGroup* cmTerraingroup;
           Ogre::Terrain* cterrain;
           float* cheightdata;
           double cselsize;
           Ogre::Vector3 cpos;
           //std::vector<Ogre::Vector3> getSelectionVerts(void);
 };
 Terrainheightselection::Terrainheightselection(Ogre::TerrainGroup* mTerraingroup, Ogre::Vector3 pos, double selsize){
      cmTerraingroup = mTerraingroup;
      cterrain = cmTerraingroup->getTerrain(0,0);
      cheightdata = cterrain->getHeightData();
      cselsize = selsize;
      cpos = pos;
      std::vector<Ogre::Vector3> selverts = getSelectionVerts();
      Ogre::Log* tlog = Ogre::LogManager::getSingleton().getLog("test.log");
      std::ostringstream ss;
      ss<< sizeof(cheightdata);
      tlog->logMessage(ss.str());
      ss.str(std::string());
      ss<< cheightdata[9];
      tlog->logMessage(ss.str());
      Ogre::Real x = cterrain->getSize();
      ss.str(std::string());
      ss<< x;
      tlog->logMessage(ss.str());
      float xd = cterrain->getHeightAtWorldPosition(200,3000,200);
      ss.str(std::string());
      ss<< xd;
      tlog->logMessage(ss.str());
      ss.str(std::string());
      ss<< selverts.size();
      tlog->logMessage(ss.str());
 }
 std::vector<Ogre::Vector3> Terrainheightselection::getSelectionVerts(void){
      Ogre::Real posx, posz;
      Ogre::Real posxmin, posxmax, poszmin, poszmax;
      posx = cpos.x;
      posz = cpos.z;
      posxmin = posx - cselsize;
      posxmax = posx + cselsize;
      poszmin = posz - cselsize;
      poszmax = posz + cselsize;
      std::vector<Ogre::Vector3> veccont;
      for (int i = posxmin; i<posxmax+1; i++){
           for(int j = poszmin; j<poszmax+1; j++){
                double dist = pow(pow(i-posx,2)+pow(j-posz,2),.5);
                if (dist <= cselsize){
 //                    float y = cterrain->getHeightAtWorldPosition(i,3000,j);
                     Ogre::Vector3 retvec;
                     cterrain->getPoint(i,j,&retvec);
                     veccont.push_back(Ogre::Vector3(i,retvec.y,j));
                }
           }
      }
      return veccont;
 }

This class iterates a set of terrain space points using a boundary geometry (square) which the selection tool is confined inside (a circle can be confined in a square whose sides center coincides with the circle's center and whose sides are a distance of 2 * r  where r is the radius of the circle.

I iterate this confining geometry of points to avoid needless computations of points outside such geometry making both point selection faster.  I then point test by checking the the distance such point is less than the falloff radii of the terrain tool (default or user supplied).

Here's a code snippet implementation inside my mouse left button pressed listener

 rlterrVector = Ogre::Vector3(ovec.x,theight,ovec.z);  

           //conver rlterrVector to terrainspace vector
           Ogre::Vector3 rlterrVectorts;
           Ogre::Terrain* cterrain = mTerrainGroup->getTerrain(0,0);
           cterrain->getTerrainPosition(rlterrVector,&rlterrVectorts);
           //terrain space coordinate stored like world space
           crlterrVectorts = Ogre::Vector3(513*rlterrVectorts.x,rlterrVectorts.z,513*rlterrVectorts.y);
           Terrainheightselection* a = new Terrainheightselection(mTerrainGroup, crlterrVectorts,
                                      falloffwidth);
           falloffSelection = a->getSelectionVerts();



In this case I converted the given fixed terrain selection spot, that is, where the user pressed the left mouse button on the given viewport and its corresponding intersection on the given terrain topology, and I converted this from a corresponding world space terrain coordinate selection point (see my blog post on terrain ray query ray testing for tutorial on terrain ray query testing workaround) to terrain space coordinates, and then I pass this to the Terrainheightselection class and grab the selection vertices. You should supply to the this class a position that is in terrain space coordinates but has world space ordering that is, TSvec = (TSvec.x, TSvec.z, TSvec.y)  as is shown above.

I haven't robustly written this by the way for terrain edges (not sure if this will crash if you are for instance adjusting terrain nearest the terrain's edge).

Ogre falloff 2 node cspline interpolation class

  #include <OgreMatrix4.h>  

 class Falloffinterpolate{
      public:
           double tparam, crfalloff, ctramount;
           Ogre::Vector3 cpoint;
           Ogre::Vector3 cipoint; //interpolated position
           Ogre::Vector3 ccpoint; //center point for falloff radii
           Ogre::Vector4 cicoeff;
           Falloffinterpolate(Ogre::Vector3 point, Ogre::Vector3 clpoint, double rfalloff, Ogre::Vector4 icoeff, double tramount);
           Falloffinterpolate(std::vector<Ogre::Vector3> falloffSelection, Ogre::Vector3 clpoint,
           double rfalloff, Ogre::Vector4 icoeff, double tramount);
           ~Falloffinterpolate(void);
           Ogre::Vector3 getIpoint(void);
           std::vector<Ogre::Vector3> getIpoints(void);
      private:
           double computetparam(void); //normalized distance measure from ccpoint to cpoint.
           Ogre::Vector3 computeinterp(void);
           std::vector<Ogre::Vector3> cfalloffSelection;
           //std::vector<Ogre::Real> Ipoints;
           std::vector<Ogre::Vector3> Ipoints;
 };
 Falloffinterpolate::Falloffinterpolate(Ogre::Vector3 point, Ogre::Vector3 clpoint, double rfalloff, Ogre::Vector4 icoeff, double tramount){
      cpoint = point;
      ccpoint = clpoint;
      cicoeff = icoeff;
      crfalloff = rfalloff;
      ctramount = tramount;
      tparam = computetparam();
      cipoint = computeinterp();
      Ogre::Log* tlog = Ogre::LogManager::getSingleton().getLog("test.log");
      std::ostringstream ss;
      ss<<"Faloffinterpolation********\n";
      ss<<"cpoint: "<< cpoint << "\n";
      ss<<"ccpoint: "<< ccpoint << "\n";
      ss<<"ctramount: "<< ctramount << "\n";
      tlog->logMessage(ss.str());
 }
 Falloffinterpolate::Falloffinterpolate(std::vector<Ogre::Vector3> falloffSelection, Ogre::Vector3 clpoint, double rfalloff, Ogre::Vector4 icoeff, double tramount){
      //cpoint = point;
      cfalloffSelection = falloffSelection;
      ccpoint = clpoint;
      cicoeff = icoeff;
      crfalloff = rfalloff;
      ctramount = tramount;
      Ipoints.resize(falloffSelection.size());
      std::ostringstream ss;
      for (int i = 0; i < falloffSelection.size(); i++){
           cpoint = falloffSelection[i];
           tparam = computetparam();
           cipoint = computeinterp();
           Ipoints[i] = Ogre::Vector3(cipoint.x,cipoint.y,cipoint.z);
           //ss<<"tparam: "<<tparam<<"\n";
           //ss<<"translate point: "<<cipoint.y<<"\n";
      }
      //tparam = computetparam();
      //cipoint = computeinterp();
      Ogre::Log* tlog = Ogre::LogManager::getSingleton().getLog("test.log");
      ss<<"Faloffinterpolation********\n";
      ss<<"cpoint: "<< cpoint << "\n";
      ss<<"ccpoint: "<< ccpoint << "\n";
      ss<<"ctramount: "<< ctramount << "\n";
      ss<<"coefficients: "<<cicoeff<<"\n";
      ss<<"compute 2^2: " << pow(-2.0,2) << "\n";
      ss<<"coefficent w: " << cicoeff.w << "\n";
      tlog->logMessage(ss.str());
 }
 Falloffinterpolate::~Falloffinterpolate(){
 }
 double Falloffinterpolate::computetparam(){
      //tparam is normalized relative crfalloff. This should be a value between 0 and 1.
      // distance measure for tparam is done on xz plane strictly...we neglect height
      //when selecting a set of vertices.
      Ogre::Log* tlog = Ogre::LogManager::getSingleton().getLog("test.log");
      std::ostringstream ss;
      ss << "point 1: "<< cpoint.x<<","<< cpoint.z << "\n";
      ss << "center point: "<< ccpoint.x<< ","<<ccpoint.z << "\n";
      tlog->logMessage(ss.str());
      return 1-(pow(pow(abs(cpoint.x - ccpoint.x), 2)+pow(abs(cpoint.z - ccpoint.z), 2), .5) / crfalloff);
 }
 Ogre::Vector3 Falloffinterpolate::computeinterp(){
      //f(x_i)=a+bx_i+cx_i^2+dx_i^3. is the ordering on coefficients
      //for the 2 node cspline method that we call upon.
      double x = cicoeff[0] + cicoeff[1]*abs(tparam) + cicoeff[2]*pow(tparam,2) + cicoeff[3]*pow(abs(tparam),3);
      //next we rescale this by the translation factor ctramount.
      double hpos = abs(x)*ctramount; //or height position
      //double hpos = tparam*ctramount;
      //next we translate this to the existing position and return this.
      //Ogre::Log* tlog = Ogre::LogManager::getSingleton().getLog("test.log");
      //std::ostringstream ss;
      //ss << "translate amount: "<< hpos << "\n";
      //tlog->logMessage(ss.str());
      return Ogre::Vector3(cpoint.x, cpoint.y+hpos, cpoint.z);
 }
 Ogre::Vector3 Falloffinterpolate::getIpoint(){
      return cipoint;
 }
 std::vector<Ogre::Vector3> Falloffinterpolate::getIpoints(){
      return Ipoints;
 }

This basically takes coefficient data, alongside selected vertices, presumed already determined as within a given fall off radius, and then position interpolates the height map data by a given translation amount for a falloff curvature type (computed with a call to the cspline class in the previous post).

Here's some instancing snippet:

                 Ogre::Vector3 trheight = planeint->getHeight();  

                Ogre::Terrain* cterrain = mTerrainGroup->getTerrain(0,0);
                cterrain->dirty();
                Ogre::Log* tlog = Ogre::LogManager::getSingleton().getLog("test.log");
                std::ostringstream ss5;
                Falloffinterpolate fint = Falloffinterpolate(falloffSelection, crlterrVectorts, falloffwidth, falloffcoeff, (double) trheight.y);
                std::vector<Ogre::Vector3> newpos1s = fint.getIpoints();
                for (int i = 0;i < falloffSelection.size();i++){
                     Ogre::Vector3 pos1 = falloffSelection[i];
                     //ss5<<"Pre Interpolated position: "<< pos1<<"\n";
 //                    Falloffinterpolate fint = Falloffinterpolate(pos1, rlterrVector, 20.0, falloffcoeff, (double) trheight.y);
 //                    Ogre::Vector3 newpos1 = fint.getIpoint();
                     //ss5<<"Interpolated position: "<< newpos1s[i] <<"\n";
                     Ogre::Vector3 outvec(0,0,0);
                     Ogre::Vector3* outvecp = &outvec;
                     //const Ogre::Vector3 invec = Ogre::Vector3(pos1.x, newpos1s[i],pos1.z);
                     //const Ogre::Vector3& invecp = &invec;
                     //cterrain->getTerrainPosition(invec,&outvec);
                     cterrain->setHeightAtPoint (newpos1s[i].x, newpos1s[i].z, newpos1s[i].y);
                }
                ss5<<"Translate Height: "<< trheight.y<<"\n";
                tlog->logMessage(ss5.str());
                ss5.str(std::string());
                cterrain->update();


I have a lot of commenting here mostly for reading test data.
the falloffSelection object above is a std::vector container containing all selected vertices given by terrain position...not world position coordinates but sort organized by world coordinate ordering and not by terrain ordering...namely Vector3(terrainspace.x, terrainspace.z, terrainspace.y) 

crlterrVectorts is the terrain space coordinate world space coordinate ordering Ogre::Vector3 object which represents the given point for terrain translation (this represents the given terrain point in which are translation fall off radii is centered for the given translation tool.

falloffwidth I have actually adapted this with OIS::KC_F1 and OIS::KC_F2 calls in this test for increment and decrement units += 10 terrainspace units.

(double) trheight.y  I have covered the mouseviewport ray plane intersection test in a previous post on how to obtain a user supplied translation height...keep in mind that I have instantiated this interpolation class in the mousemoved listener so that the user can real time change translation data (akin to blender's translation graphical functionality).

Its worth mentioning that I parameterize the distance measure of any point within the falloff selection scope by dividing the distance of such point with the center falloff by the overall fall off radius, and then I subtract this parametric quantity by 1 to invert the selection...this ensures that the center of the fall off is given to full translation while the boundaries at full fall off radii distance have zero translations, and all other points are translated according to the parametric position (between 0 and 1) of the fall off curve where those closest to the out boundary of the falloff radii are given to less translation relative to the those closest to the center.  This reflected in the parametric choice in representing such point as described above.

On a final note, I convert to terrain space avoiding world space computations for translations here since ultimately it appears the setPoint() type function for Ogre's terrain system is done in terrain space and not world space.  Thus if having worked terrain translations using World space coordinates likely I'd have much finer gradation in point translation then necessary which would ultimately be truncated in the conversion to terrain coordinate system and then using the set methods for assigning coordinate heights, or in other words, if using world space one would have likely acquired needless points to this computation.

Don't forget that when converting to terrain space coordinates (check API on this) terrain coordinates are yielded with a base vector on the x,y or corresponding x,z world space coordinate.  A base vector or otherwise synonymous in mathematics with a normalized vector, has numeric ranges between 0 and 1 where 1 represents the maximal coordinate position on the terrain, or in other words with a terrain of size 513 x 513 a base vector of coordinate (1,1) would be position (512,512) on such terrain space (not a base terrain coordinate) The set method however utilizes non base terrain coordinates so you'll need to multiply the base vector x,y terrain coordinates by the terrain size, or in the tutorial's case 513...don't multiply the height value since this remains unchanged through world space and terrain space coordinate systems..

Thursday, December 4, 2014

An Ogre Cspline class that I created for 2 node c spline computations

 #include <vector>  

 #include <OgreMatrix4.h>
 #include <OgreVector4.h>
 using namespace std;
      class cspline
      {  
        public:
           vector<double> xc;
           vector<double> yc;
           vector<double> ypc; //y prime boundary conditions
           //var Coeff =Ogre::Matrix4.Build(4,4);
           cspline (vector<double> x, vector<double> y);
           cspline (vector<double> x, vector<double> y, vector<double> yp);
           Ogre::Vector4 compute2nodecspline (double bc1, double bc2);
           vector<Ogre::Vector4> computecspline (void);
           vector<Ogre::Vector4> computecsplinebcs (void);
           Ogre::Matrix4 coeffmatrix(double x1, double x2, double y1, double y2);
           Ogre::Matrix4 coeffmatrixi(int iter1);
           Ogre::Vector4 simplesfuncvector(int iter1,int iter2, double bc1, double bc2);
           Ogre::Vector4 splinefuncvector(int iter1,int iter2);
           Ogre::Vector4 csplinecoeff(Ogre::Matrix4 M, Ogre::Vector4 y);
           vector<double> computeslope(int iter1, int iter2);
      };
           cspline::cspline (vector<double> x, vector<double> y)
           {    
 /*    
                     if (x.Length != y.Length)
                     {
                          throw new ArgumentException ("Vectors are not the same length!");
                     }
                     if (x.Length < 2)
                     {
                          throw new ArgumentException ("vector array needs to be of length greater than 1!");
                     }
 */                    
                     //xc[sizeof(x)];
                     //yc[sizeof(y)];
                      //std::copy(std::begin(x), std::end(x), std::begin(xc));
                     //std::copy(std::begin(y), std::end(y), std::begin(yc));
                     xc=x;
                     yc=y;
           }
           cspline::cspline (vector<double> x, vector<double> y, vector<double> yp)
           {          
 /*
                if (x.Length != y.Length || x.Length != yp.Length)
                {
                     throw new ArgumentException ("Vectors are not the same length!");
                }
                if (x.Length < 2)
                {
                     throw new ArgumentException ("vector array needs to be of length greater than 1!");
                }
 */
                //copy(std::begin(x), std::end(x), std::begin(xc));
                //copy(std::begin(y), std::end(y), std::begin(yc));
                //copy(std::begin(yp),std::end(yp),std::begin(ypc));
                xc=x;
                yc=y;
                ypc=yp;
           }
           Ogre::Vector4 cspline::compute2nodecspline (double bc1, double bc2){
                //Ogre::Vector4 vcont = Ogre::Vector4.Build.Dense (2);
                Ogre::Vector4 x = Ogre::Vector4(0,0,0,0);
                for (int i=0; i< sizeof(xc); i++) {
                     if (i <sizeof(xc)-1){
                          Ogre::Matrix4 M = coeffmatrix (xc[i],xc[i+1],yc[i],yc[i+1]);
                          //Ogre::Vector4 y = splinefuncvector(i,i+1);
                          Ogre::Vector4 y = simplesfuncvector(i,i+1,bc1, bc2);
                          Ogre::Vector4 x = csplinecoeff(M,y);
                          return x;
                          //vcont = x;
                     }
                }
                return x;
           }
           vector<Ogre::Vector4> cspline::computecspline (){
                //I compute first derviative boundary conditions. You can modify this code for second or third
                //derivative boundary conditions, but would have to supply your methods for computing second
                //or third derivatives on your own, likewise, you'd need to make adjustment to the coefficient
                //matrix according to the solution method indicated below...that is, with higher order derivatives
                //adjusting the matrix with zeros and appropriate derivative order on the interpolating row equation
                //f(x_i)=a+bx_i+cx_i^2+dx_i^3.
                //int iter1 = 0;
                vector<Ogre::Vector4> vcont(sizeof(xc));
                for (int i=0; i< sizeof(xc); i++) {
                     if (i <sizeof(xc)-1){
                          Ogre::Matrix4 M = coeffmatrixi (i);
                          Ogre::Vector4 y = splinefuncvector(i,i+1);
                          Ogre::Vector4 x = csplinecoeff(M,y);
                          vcont[i] = x;
                     }
                }
                return vcont;
           }
           vector<Ogre::Vector4> cspline::computecsplinebcs (){
                // First derviative boundary conditions supplied and assumed provided with constructor.
                //You can modify this code for second or third
                //derivative boundary conditions, but would have to supply your methods for computing second
                //or third derivatives on your own, likewise, you'd need to make adjustment to the coefficient
                //matrix according to the solution method indicated below...that is, with higher order derivatives
                //adjusting the matrix with zeros and appropriate derivative order on the interpolating row equation
                //f(x_i)=a+bx_i+cx_i^2+dx_i^3.
                //int iter1 = 0;
                vector<Ogre::Vector4> vcont(sizeof(xc));
                for (int i=0; i< sizeof(xc); i++) {
                     if (i <sizeof(xc)-1){
                          Ogre::Matrix4 M = coeffmatrixi (i);
                          //Ogre::Vector4 y = splinefuncvector(i,i+1);
                          Ogre::Vector4 y = simplesfuncvector(i,i+1, ypc[i], ypc[i+1]);
                          Ogre::Vector4 x = csplinecoeff(M,y);
                          vcont[i] = x;
                     }
                }
                return vcont;
           }
           Ogre::Matrix4 cspline::coeffmatrix(double x1, double x2, double y1, double y2)
           {    
                //coefficients are determined for the cubic equation of the following order
                // f(x)= a + bx + cx^2+dx^3 and f'(x)=0+b+2cx+3dx^2
                // where f(x_i)=a+bx_i+cx_i^2+dx_i^3 and f'(x_i)=a*0+b+2cx_i+3dx_i^2 determine the rows of the
                //coefficient matrix of the spline interpolation matrix between node points.
                double coeffvals[4][4];
                for (int i=0; i < 2; i++) {
                     //double[] coeffrow= new double[4];
                     for (int j=0; j < 4;j++){
                          coeffvals[i][j]=pow(xc[i],j);
                          //Console.WriteLine (coeffvals[i,j]);
                     }
                     //coeffvals[i,j]=coeffrow;
                }
                for (int k=0; k < 2; k++) {
                     //double[] coeffrow= new double[4];
                     //Console.WriteLine (i);
                     for (int l=0; l < 4;l++){
                          if (l==0){
                               coeffvals[k+2][l]=0;
                          }
                          else {
                               coeffvals[k+2][l]= l*pow(xc[k],l-1);
                          }
                     }
                     //coeffvals[i]=coeffrow;
                }
                Ogre::Matrix4 M = Ogre::Matrix4(coeffvals[0][0], coeffvals[0][1], coeffvals[0][2],coeffvals[0][3],
                                    coeffvals[1][0],coeffvals[1][1],coeffvals[1][2],coeffvals[1][3],
                                    coeffvals[2][0],coeffvals[2][1],coeffvals[2][2],coeffvals[2][3],
                                    coeffvals[3][0],coeffvals[3][1],coeffvals[3][2],coeffvals[3][3]);
                return M;
           }
           Ogre::Matrix4 cspline::coeffmatrixi(int iter1)
           {    
                //coefficients are determined for the cubic equation of the following order
                // f(x)= a + bx + cx^2+dx^3 and f'(x)=0+b+2cx+3dx^2
                // where f(x_i)=a+bx_i+cx_i^2+dx_i^3 and f'(x_i)=a*0+b+2cx_i+3dx_i^2 determine the rows of the
                //coefficient matrix of the spline interpolation matrix between node points.
                double coeffvals[4][4];
                for (int i=0; i < 2; i++) {
                     //double[] coeffrow= new double[4];
                     for (int j=0; j < 4;j++){
                          coeffvals[i][j]=pow(xc[iter1+i],j);
                          //Console.WriteLine (coeffvals[i,j]);
                     }
                     //coeffvals[i,j]=coeffrow;
                }
                for (int k=0; k < 2; k++) {
                     //double[] coeffrow= new double[4];
                     //Console.WriteLine (i);
                     for (int l=0; l < 4;l++){
                          if (l==0){
                               coeffvals[k+2][l]=0;
                          }
                          else {
                               coeffvals[k+2][l]= l*pow(xc[iter1+k],l-1);
                          }
                     }
                     //coeffvals[i]=coeffrow;
                }
                Ogre::Matrix4 M = Ogre::Matrix4(coeffvals[0][0], coeffvals[0][1], coeffvals[0][2],coeffvals[0][3],
                                    coeffvals[1][0],coeffvals[1][1],coeffvals[1][2],coeffvals[1][3],
                                    coeffvals[2][0],coeffvals[2][1],coeffvals[2][2],coeffvals[2][3],
                                    coeffvals[3][0],coeffvals[3][1],coeffvals[3][2],coeffvals[3][3]);
                return M;
           }
           Ogre::Vector4 cspline::simplesfuncvector(int iter1,int iter2, double bc1, double bc2){
                //with supplied boundary conditions on a 2 node set
                double rvec[4];
                rvec [0] = yc [iter1];
                rvec [1] = yc [iter2];
                //double[] slopes = computeslope (iter1, iter2);
                rvec [2] = bc1;
                rvec [3] = bc2;
                Ogre::Vector4 V = Ogre::Vector4(rvec[0],rvec[1],rvec[2],rvec[3]);
                return V;
           }
           Ogre::Vector4 cspline::splinefuncvector(int iter1,int iter2){
                double rvec[4];
                rvec [0] = yc [iter1];
                rvec [1] = yc [iter2];
                vector<double> slopes = computeslope (iter1, iter2);
                rvec [2] = slopes [0];
                rvec [3] = slopes [1];
                //Ogre::Vector4 V = DenseVector.OfArray(rvec);
                Ogre::Vector4 V = Ogre::Vector4(rvec[0],rvec[1],rvec[2],rvec[3]);
                return V;
           }
           Ogre::Vector4 cspline::csplinecoeff(Ogre::Matrix4 M, Ogre::Vector4 y){
                Ogre::Matrix4 invmatrix = M.inverse();
                for (int i = 0; i < 4; i++){
                     for(int j =0; j < 4; j++){
                          if (abs(invmatrix[i][j])<1e-6){
                               invmatrix[i][j] = 0;
                          }
                     }
                }
                Ogre::Vector4 x = invmatrix*y;
                return x;
           }
           vector<double> cspline::computeslope(int iter1, int iter2){
                //computing boundary condition first dervative slope conditions. This is an average slope approaching from
                // an exterior point...that is not from (y2-y1)/(x2-x1) but a previous iteration point
                //first we check to see if the point is on our node boundary of points, if so we assign a slope of zero
                double slope1;
                double slope2;
                vector<double> slopes(2);
                if (iter1 == 0) {
                     slope1 = 0.0;
                }
                else
                {
                     slope1 = (yc[iter1]-yc[iter1-1])/(xc[iter1]-xc[iter1-1]);
                }
                if (iter2 == sizeof(xc)-1) {
                     slope2 = 0.0;
                }
                else
                {
                     slope2 = (-yc[iter2]+yc[iter2+1])/(-xc[iter1]+xc[iter1+1]);
                }
                slopes [0] = slope1;
                slopes [1] = slope2;
                return slopes;
           }
*************************************


Here's an example Ogre based instancing of the class

   cspline* cs = new cspline(twopoints,twopoints);  
    std::vector twopoints(2);
    twopoints[0] = 0;
    twopoints[1] = 1;
   double tan1 = (double)Ogre::Math::Tan(Ogre::Math::DegreesToRadians(30));
   falloffcoeff = cs->compute2nodecspline (tan1, tan1);

I haven't tested this in the generalized n node computation case, but program gives an idea as to how this is done.

Basically two boundary conditions are supplied which in this case are first order derivatives, or slopes at either point (0,0) and (1,1).

I use an array style call by the way to Ogre::Vector4 containers since Vector4.w,...,Vector.z
ordering is not alphabetically speaking similar by order principle in other words Vector4.w is mapped to Vector4[3].

By the way for the given boundary conditions supplied this yields the following equation

0.57735x +1.26795x^2-0.845299x^3 from 0 to 1

or


for the given falloff plot.

As indicated in the notes for computing coefficients in the class.  The coefficients are given by the following ordering

where cfvect is the Ogre::Vector4 coefficient vector from the cspline class

means the cubic equation would have the following form

f(x) = cfvect[0] + cfvect[1]*x +cfvect[2]*x^2+cfvect[3]*x^3

I always recommend testing a cubic equation for the desired parameters.  If you hadn't readily had online mathematical plotting software or something in Ogre immediately handy for doing this.  Wolfram Alpha provides free online software for checking a plot.

I'd mention you can instance your cspline falloff computations on the build scene method or somewhere at the program's outset, but you wouldn't need to re compute since the curve is fixed and that we parameterize/rescale the distance metric for all selected vertices.  In fact, you can likely build a library of fall off curves that can be referenced in the interpolating class method call, you just need to supply the appropriate curve coefficients from such pre compiled library. 

A quick Ogre C++ API reading translation tip if you are a noob

http://www.ogre3d.org/docs/api/1.9/

So if you were wanting to understand reading a C++ api and understanding how to invoke say class method calls say from class pointer objects or class objects, I'll provide a little reading translation help here.

Firstly in C++ if you weren't already familiarized relative other Object oriented programming languages you'd notice the '->' being featured for method calls.   Obviously you learn after the first or second crash day of C++ that this '->' call is like the '.' call for invoking a call to class method or in particular a class pointer object.  Thus if having instantiated a class pointer object say

Ogre::TerrainGroup* mTerrainGroup;

Where Ogre::TerrainGroup* is a pointer object to Ogre::TerrainGroup

Anytime a pointer object is instantiated class pointer object invocations are done with '->' as opposed to '.'
Otherwise if it were possible to declare and instantiate

Ogre::TerrainGroup mTerrainGroup;

where mTerrainGroup were a Ogre::TerrainGroup object we'd use a '.' after such object in referencing its method/function call.

Often times when referencing say the Ogre API for methods of a given class object, we can see the method call given in a given addressing format...

thus

void Ogre::Terrain::getPoint ( long  x,
long  y,
Vector3 outpos 
) const

the first token 'void' is the return type of the function call here.

the second and third token bridged by '::' references the Ogre addressing namespace alongside the class object type which is a 'Terrain' object.  Often times when you are declaring a new Ogre object, you'd typically have some referencing like 'Ogre::ogre_object_name    yourobjnamehere;'

Sometimes you'll see return types other than 'void' as indicated above returned where you can store such return object and neither having to supply some output object (in the above case it is a 'Ogre::Vector3*'  object that is required so that the method can provide a given point output to this object).

The fourth token 'getPoint' is the method name of the 'Ogre::Terrain' object...
according to ordering principles, you'd likely find method names are always delimited to the right by the '(' parentheses token.

Returning back to the example above, also if I have a given Ogre::Terrain object instantiated or a object pointer then I can reference method calls to such object by using the '.' or '->' followed by the method name

thus an object

Ogre::Terrain cterrain;
Ogre::Vector3 outputvec;

would have a method call to getPoint() as

cterrain.getPoint(0,0,&outputvec);

or

Ogre::Terrain* cterrain;
Ogre::Vector3 outputvec;
as

cterrain->getPoint(0,0,&outputvec);

Another common feature found are overloaded constructors for a given object, read through these since there may be many different ways to instantiate a given Ogre object using any variation of Ogre objects or whatever necessary to instantiate the given object.  Basically if you weren't familiar with overloading, overloading say could be as simple as allowing you to do addition on two number objects say even if they were floats or doubles, Ogre::Real and so forth so that you hadn't need worry about having a specific object type to instantiate or invoke the class object or method, or in other words less work tracking and type cast converting an object from one type to another in order to instantiate a given object type (through object constructor overloading).  Another example, An Ogre::Vector has 6 different instantiation constructors, or there are six different ways to declare and instance an Ogre::Vector (outside of pointers and anything else).

Also read through required supplied parameters/objects required to get an object up and running or invoking a given object method.

I always recommend a thorough reading of any API concerning a given Object type since outside of code recipes since this can give you a more thorough working knowledge on implementing structures or using fully the tools that such object provides so that you hadn't need do added legwork either re inventing structures by added work or anything else.




Code excerpt for updating Ogre terrain height positions and some added notes

   Ogre::Terrain* cterrain = mTerrainGroup->getTerrain(0,0);  
   ss5<<"Terrain Size: "<< cterrain->getSize()<<"\n";  
   ss5<<"Maximum Height: "<< cterrain->getMaxHeight()<<"\n";  
   ss5<<"World Size: " << cterrain->getWorldSize()<<"\n";  
   for (int i=0; i<250; i++ ){  
      for (int j=0; j<250; j++){  
        ss5 << "Terrain position("<<i<<","<<j<<")"<<cterrain->getHeightAtPoint(i,j)<<"\n" ;  
        cterrain->setHeightAtPoint(i,j,cterrain->getHeightAtPoint(i,j)+100);  
      }  
   }  
   cterrain->update();  

The above is a code snippet for updating terrain height coordinates in Ogre 1.9x using Ogre's Terrain manager system.

Some bit of things to keep in mind with Ogre's terrain system is that like Unity and I imagine other game engine's there are two separate coordinate systems namely:  world coordinate system, and local terrain coordinates.  A given tutorial for instance through Ogre Tutorial's typically load the terrain with a world coordinate dimension ranges of 12,000 x 12000 while local terrain coordinate dimension's are 513 x 513.   As related to my previous post on the subject of extracting a terrain world coordinate position from a given ray trace query, you'd want to keep in mind converting from a world coordinate to local terrain coordinate position.   Ogre provides a function to do this

void Ogre::Terrain::getTerrainPosition ( const Vector3 WSpos,
Vector3 outTSpos 
) const


or practice example

                     Ogre::Vector3 outvec(0,0,0);  

                     Ogre::Vector3* outvecp = &outvec;
                     const Ogre::Vector3 invec = Ogre::Vector3(pos1.x,pos1.z, newpos1s[i]);
                     //const Ogre::Vector3& invecp = &invec;
                     cterrain->getTerrainPosition(invec,&outvec);

Wednesday, December 3, 2014

Ogre: Notes on building a Terrain Raise/Lower Tool for a terrain tool editor

The idea pretty much while extending from some previously mentioned posts on mathematics concerning things like building a falloff for the tool in terms of raising or lowering a given terrain (see the example for computing 2 node cubic spline coefficients in a previous blog post of mine as of not too long ago).  It appears that basic and intermediate ogre tutorials provide an excellent foundation for a project such as this.  Ogre does also provide some simple interpolating smoothing functions as well, although I am not sure that you could amply customize this relative to other known methods such as the one that I've mentioned, so likely you may want to adapt (as I have done) from another game engine a given package or find a working package for c++ that does this bit math work.  Actually Ogre does provide some basic linear algebra functionality as well to do computations...for instance, Ogre::Matrix4, Ogre::Matrix3 and so forth coupled with Ogre::Vector4, and so forth which can be used for Matrix and vector products, or computing the .inverse() of a given matrix.

Adaptation legwork generally comes in the form of the design of the selection tool.  For instance, in my case, I decided to use from a given a ray to terrain intercepted point, a bounding of box of dimension 2 * radius on both sides (a square) and then from the bounding planar square, iterate the set of points inside this in determining for a given bounding geometry (in my case a circle) all the points falling inside the given tools falloff radius or 1/2 the length of the square where such points given from the distance of the center of the square are less than such fall off radius.  By using a bounding square (box) or geometry, obviously this avoids needless computations through point sets that would certainly fail otherwise.  Another selection method adapts intermediate tutorial 4, where one creates a bounding volume and then determines the points of intersections using this (likely one would need to manually handle the actual ray bounding volume query process (if using Ogre 1.9) since WorldTerrainFragments appears to be non functioning for terrain ray query processes. Unfortunately, this also still remains a little bit of greek for me, so I wouldn't be able to describe how methods would be drawn up in detail on this method especially in the way of bounding volumes.

Once having the selected vertices.  Two possibilities that I have constructed for such tool, or user configured settings terrain height translation (say, where the user can set the tool to increment with each mouse click in increments either raising or lowering such), or alternately a different method, which approximates other 3d modelers which is allowing the user to while holding the mouse button down control the rate of translation vertically all in one shot until the user depresses the mouse button key.    Again for translating points (as adapted from another blog post of mine on this subject matter), one can with a fixed falloff type pre compute on scene loading or at any time when the falloff type is selected pre compute say cspline coefficients between a normalized point set (something like points (0,0) and (1,1)) and then simply normalize the t distance parameter for such point in the given translation fall off range.  Normalizing I've found is pretty simple which is to say computing the distance between the two points and then dividing by the overall fall off radii...if you need to invert the normal t parameter you can simply use tparam* = 1-t param so that the center points is actually given to a t param* value of 1 and vice versa the most distant points are at zero (indicate no translated change in elevation at this point).  Once having the t param or t param * value then one need simply compute the corresponding fall off position using the cspline coefficient equation, and then having applied a translation scale factor or the amount that a user has selected by raising the elevation of the mouse in a given viewport.

This raises the last remaining obstacle to the problem considered here.  Since finding the view port elevation coordinates is another mathematical problem.  One could simply grab mouse coordinates but the problem obviously with this is if say grabbing the viewport to mouse pointer ray means that choosing the origin of the ray means that elevation control isn't exactly aligned say to a locked ray intercept position on such terrain, and then finessing the terrain with the mouse cursor active for terrain translation is definitely more cumbersome without projecting the position of the cursor so that it is more finely tuned to a given selected terrain position and all surrounding points.

My thought solution (not implemented as of yet), however, considers this problem by creating a perpendicular plane relative to the camera's active viewport that changes even if the viewport changes orientation relative to a terrain locked coordinate.  In this case, we can actually use the original mouse ray and from this select the ray's direction to be of aid here, and from this creating a perpendicular to viewport bounding plane that passes through the locked coordinate terrain point.

So I believe in finishing this problem, one should make use of the coordinate point, and from this take the original viewport ray's direction and rotate this as desired by 90 degrees...a  the viewport ray direction I believe is given by a three tuple vector coordinate values , construct a ray from say the locked terrain position (as the ray's origin) and then advance it, arbitrarily as desired in finding another point, or alternately you can setup the math computations (say trig or something else if desired).  Another point can similarly be yielded using an alternate 90 degree rotation on the desired coordinate axis from such direction vector with a rotation.  Ogre provides as with a lot of game programming interfaces Quaternion computations, or handy way of performing rotations Quaternion algebra (product of rotations here), and then converting this rotation back into a direction vector.

Here's another simple code recipe:

PlaneIntersection::PlaneIntersection(Ogre::Vector3 pos, Ogre::Ray mouseRay){
//Ogre::Ray topLeft = mCamera->getCameraToViewportRay(left, top);
Ogre::Vector3 dirvec = mouseRay.getDirection();
//could use quarternions, but we can get orthogonal vectors, assuming
// local x is in the direction of such vector
// that a z rotation (2d) and a y rotation should yield orthogonal
//vector components.
Ogre::Real sinsq = Ogre::Math::Sin(Ogre::Math::DegreesToRadians(90));
Ogre::Real cossq = Ogre::Math::Cos(Ogre::Math::DegreesToRadians(90));
//Ry = {{cost,0,sint},{0,1,0},{-sint,0,cost}}
Ogre::Matrix3 Roty = Ogre::Matrix3(cossq,0,sinsq,0,1,0,-1*sinsq,0,cossq);
//Rz = {{cost,-sint,0},{sint,cost,0},{0,0,1}}
Ogre::Matrix3 Rotz = Ogre::Matrix3(cossq,-1*sinsq,0,sinsq,cossq,0,0,0,1);
Ogre::Vector3 dirvecy = Roty*dirvec;  //rotated on y axis
Ogre::Vector3 dirvecz = Rotz*dirvec;  //rotated on z axis
Ogre::Ray rayy = Ogre::Ray(pos,dirvecy);
Ogre::Ray rayz = Ogre::Ray(pos,dirvecz);
Ogre::Vector3 point2 = rayy.getPoint(3);
Ogre::Vector3 point3 = rayz.getPoint(3);

Ogre::Plane tplane = Ogre::Plane(pos, point2, point3);         // front plane
std::pair test = mouseRay.intersects(tplane);
std::ostringstream ss;
ctest = test.first;
if (test.first){
ss<<"Distance to intersection: "< Ogre::Real tdist = test.second;
Ogre::Vector3 theight = mouseRay.getPoint(tdist);
cheight = theight;

}

In this case I supplied rotation matrices to a given direction vector, which provided new orthogonal direction vectors in two directions which allow for the construction of a plane that intersects the fixed raise terrain selection point and points from a given arbitrary ray cast in two different orthogonal directions relative to the originating view port mouse ray.  In this way the added points aid in constructing a plane which is parallel to the viewport and intersects the desired fixed position.

Oblivion

 Between the fascination of an upcoming pandemic ridden college football season, Taylor Swift, and Kim Kardashian, wildfires, crazier weathe...