giggidy giggidy

Published January 07, 2007
Advertisement
I myself am curious exactly how does one 'shoop woop'?

I've started a new article (or series, most likely) about random level generation from a practical perspective. Instead of taking some random topic and elaborating on it at great, windy length, I'm starting with a basic level description instead, and working from there toward a finished script. Along the way it'll discuss the techniques involved, and hopefully the context of a practical real-world level might demonstrate the techniques better.

At any rate, I came up with a level description that would hopefully demonstrate lots of techniques. I wanted mountains and flatlands, and drawing upon my memories of the Rims in Billings, Montana, I wanted a long, high cliff bisecting the entire map. And I wanted a road winding up the cliff to a fortress at the top. So I sat down with some noise generators and the height-map editor, to figure out what I had to do, and discovered that a long cliff isn't as easy as I thought given the functionality of the engine at that point in time.

So, I built a very quick, very simple little module extension to libnoise. I call it Fault. Basically, it implements a simple bisecting plane that acts as a threshold, so that the module returns either high or low (specified by user, default to 0 and 1 respectively; should probably default it to -1 and 1 to better fit with the rest of the library). On a 2D mapping of a module, I simply set the bisecting plane so that it appears to be a bisecting line, and I'm in business. I didn't like the sharp-edge, so I added a fall-off parameter that smooths it out. However, there be weirdness...

A straight cliff is boring, so in order to spice it up a bit I need to chain it with a turbulence modifier. However, turbulence plays hell with the fall-off, since I am perturbing the input coordinates of the fault module. I can't really see any way to really get a perturbed cliff with a smooth gradient change, other than performing a 2 pass--perturb a sharp fault into a buffer, then pass a blur across it to dull the edges. Which sucks ass, because it breaks the elegant chaining built into libnoise.

To sort of mitigate the chaining breakage, I implemented another libnoise module extension that maps the input into a user-specified buffer. It's a bit of a hack, since it maps a 2D buffer of my CFloatArray2D variety (the same data-type used by the scripting interface for buffer and heightmap manipulation), however for my strictly 2D purposes it works fine. It allows the user to specify a float buffer, and a range to map the buffer to (repeating), then the module can be used as part of a libnoise module chain just like any other.

So I can generate my perturbed, blurred fault map in one step, assign it to a Buffer module, then use that module as a source in further heightmap generation. Which works, but I hate having to split it into steps.

In the process of trying to figure that problem out, I came up with a third libnoise extension, the TurbulenceMasked module. Basically, it is identical to the basic Turbulence module, but it accepts a control module to be used as a power modifier for the turbulence. This control buffer typically should map to the range [0,1], and the value at the given (x,y,z) is multiplied by the turbulence module's own power modifier. This allows me to mask out or phase out turbulence in areas of a map where the control module maps to 0, and to apply full turbulence at areas where control maps to 1. And, of course, gradients in between.

The purpose of this was that I would generate a straight fault cliff, then map an externally loaded turbulence mask to a buffer module to mask out a portion of the cliff in the center of the map. This would cause turbulence to be applied to the cliff, but the turbulence would get less and less as we neared the location in the center where I was going to put the fortress and the road. These portions would later be blended into the final heightmap from pre-generated pieces edited in the heightmap editor, and the phased-out turbulence would make the edges match up.

Some time tonight, I reckon I'll post the code to the three modules in case anyone can make use of them.

At any rate, the next articles are well underway, so hopefully in the next few weeks you'll start seeing them appear.
Previous Entry 4x4
0 likes 3 comments

Comments

Gaheris
Sounds neat! I might put them (the modules) into my Java libnoise bindings library and play around with them if I have some free time (which I probably won't have).
January 08, 2007 08:08 AM
evolutional
To shoop da woop, one must first charge one's lazah
January 08, 2007 11:06 AM
JTippetts
fault.h:

#ifndef FAULT_MODULE_H
#define FAULT_MODULE_H

// A module to select from a range based on what side of a specified line the
// input point lies on.


#include <libnoise/noise.h>
#include <cmath>
namespace noise
{
    namespace module
    {
        class Fault: public Module
        {
            public:
                Fault() : Module(GetSourceModuleCount())
                {
                    SetPlane(0,0,0,-1,0,0);
                    SetEdgeFalloff(0);
                    SetRange(0,1);
                };
                virtual double GetValue(double x, double y, double z) const;
                int GetSourceModuleCount() const {return 0;};
                
                void SetPlane(double x1, double y1, double z1, double nx, double ny, double nz)
                {
                    double dist=nx*nx+ny*ny+nz*nz;
                    dist = sqrt(dist);
                    
                    normx=nx/dist; normy=ny/dist; normz=nz/dist;
                    d=((x1*normx) + (y1*normy) + (z1*normz));
                };
                
                void SetRange(double l, double h)
                {
                    low=l; high=h;
                };
                
                void SetEdgeFalloff(double ef){edge=ef;};
            protected:
                double edge;
                double d, normx, normy, normz;
                double low, high;
        };
    }
}
#endif



fault.cpp:

#include "faultmodule.h"

using namespace noise;
using namespace module;

double Fault::GetValue(double x, double y, double z) const
{
    // Solve the plane equation for the given point
    double m = (normx*x + normy*y + normz*z) - d;
    
    double dist=fabs(m);
    double mid=(high-low)*0.5;
    
    if(m<0.0)
    {
        // Point is below the plane
        if(dist<=edge)
        {
            // Within falloff, interpolate from mid->low
            double t = dist/edge;
            double val=mid + t*(low-mid);
            return val;
        }
        else
        {
            // Not within falloff, return low
            return low;
        }
    }
    else
    {
        // Point is on or above plane
        if(dist<=edge)
        {
            // Within falloff, interp from mid->high
            double t = dist/edge;
            double val = mid + t*(high-mid);
            return val;
        }
        else
        {
            // not within falloff, return high
            return high;
        }
    }
}



buffer.h:

#ifndef BUFFER_MODULE_H
#define BUFFER_MODULE_H

#include <libnoise/noise.h>
#include "floatarray2d.h"
namespace noise
{
    namespace module
    {
        
class Buffer: public Module
{
    public:
    Buffer();
    virtual ~Buffer(){};
    
    virtual double GetValue(double x, double y, double z) const;
    int GetSourceModuleCount() const {return 0;};
    
    void SetMapping(double X1, double Y1, double X2, double Y2)
    {
        x1=X1;
        x2=X2;
        y1=Y1;
        y2=Y2;
    };
    void SetBuffer(CFloatArray2D *buff){buffer=buff;};
    
    
    protected:
        CFloatArray2D *buffer;
        double x1,y1,x2,y2;
        
};
}
}


#endif



buffer.cpp:
<div cl
January 09, 2007 02:38 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement