World To Height Map Coordinates

Hello,

I’m trying to wrap my head around the various spaces being active or available in WM.
Creating generic filters or iterating over all coordinates fetching their corresponding world space coordinates works fine.
I’m having issues setting world space coordinates to a finite size. (retrieving hf coordinates)

I’m currently generating an array holding coordinates of size x.
Transforming this to local space coordinates works fine in preview or 2d/3d mode.
But when changing to layout mode I get memory allocation errors.

I’m currently dividing the TransformCoord_WorldToHF coordinates by 128, somehow my values matched the height maps values. I know this is wrong but how can
I get absolute world space coordinates relative to the height map being rendered? Is this possible?

Here’s the snippet of code i’m working with

Thanks for any help.

[code]// Create a Random Coordinate array
// Array is constructed out of world space coordinates
int generator_amount = AMOUNT;
vector generators;
srand(time(NULL));
for (int i=0; i<generator_amount; i++)
{
CoordF rint = CoordF(float(rand() % 129), float(rand() % 129));
generators.push_back(rint);
}

// Try and figure out local space coordinates and assign random value
for (int i=0; i<generators.size(); i++)
{
// convert to local space
CoordF local_loc = TransformCoord_WorldToHF(context, generators[i]);
Coord flocal = Coord(int(local_loc.x/128.0f),int(local_loc.y/128.0f));
(*map)[flocal] = 1/float(rand() % 100);
}[/code]

Thought I’d break the problem down a bit to get a better understanding of what’s going on.

When iterating over all the height map data I can check if something is within my defined tile.
But what I’d like to do is set a value based on a list of coordinates (as given above).
But in that case I need to know what the min and max world space coordinates are of the area I’m trying to render.
Only then I can assume it is safe to access a certain height map value within that range.

For example: I might have a world space coordinate with a value of 0.5 @ (2.2,1.0).
Transforming this into a height map coordinate yields a result but I need to test if that result is availble within the area I’m trying to render.
Is that possible? Otherwise I have to iterate over all the current height map values in order to test if that value is defined in the array.

Hopefully this makes more sense?..

Let me see if I can shed a little light on this for ya.

So, when your device’s Activate() is called, you receive a parameter containing a BuildContext.

BuildContext defines all of the invocation-specific parameters for your device. Please note that your device’s Activate can be called multiple times simultaneously with different build contexts from different threads.

You can call context.GetRenderQuad4F() to return a rectangle struc containing the four world space corners of your rendering area; you can call GetWorldSize() to retrieve the resolution of the render (or w() and h() access them individually).

The TransformCoord_WorldToHF and TransformCoord_HFToWorld functions are shortcuts to take the context and do the transformations necessary to take you back and forth from worldspace to local space. Local space is a 0…x-1, 0…y-1 coordinate system where (x,y) is the resolution of the heightfield in pixels. Because of this, accessing any coordinate out of this range is an error and will cause you to go past the height array extents causing a crash. Make sure you stay in bounds when writing to the array!

So if you feed the HFToWorld function a location of 0,0 for example, you are asking “What is the world-space coordinate of the bottom-left corner of the current heightfield I’m making?”. And ( w()-1, h()-1 ) is the top-right corner.

You almost always want to iterate over every pixel in the heightfield. The simplest way to know what those pixels correspond to is shown in the following (taken directly from SimpleWorldEffect.cpp included in the PDK)

	for (int y = 0; y < map->h(); y++) {
		for (int x = 0; x < map->w(); x++) {
			// find a world-space coordinate for this heightfield coordinate
			CoordF world_loc = TransformCoord_HFToWorld(context, CoordF(float(x),float(y)) );

			CoordF delta = world_loc - origin;
			// distance in worldspace from this pixel to the origin
			float distance = delta.length();

// create a cosine pattern based on distance to origin
			float result_value = (0.5f * cos(3.14159f * distance * spacing)) + 0.5f;
			(*map)[Coord(x,y)] = result_value;
		}
		context.ReportProgress(this, y, map->h());
	}

Here you can see that we’re looking up our location in world space for each pixel and using that to determine what height value to create (the resulting pattern is a pattern in world space and so is independent of the resolution you choose to create your terrain at, which is a very desirable property!)

So, now to your example.

You have your set of world space coordinates; you need to transform them to local space, and then clip the resulting coordinate against the heightfield. Anything outside of the heightfield you don’t want to attempt to draw into the heightfield.

For each point, use the TransformCoord_WorldToHF function to retrieve the local space value, then clip that value against the HF boundaries. Easiest way is to use the provided function Intersect_Point_Rect:

CoordF worldpt(.0512f, -10.2f) // for example

CoordF localpt = TransformCoord_WorldToHF(context, worldpt);

Rect4 bounds; 
bounds.x0 = 0;
bounds.y0 = 0;
bounds.x1 = w()-1;
bounds.y1 = h()-1;

if (Intersect_Point_Rect4(localpt , bounds)) {
 // in our heightfield, do something
 (*map)[localpt] = 1.0f;  // color in that point!
}
else {
// point is not within the world space extents of this heightfield.
}

So if you wanted to, for example, put a dot in the heightfield corresponding to a world space location, that would do it. If you device is respecting world space successfully, Layout View will work like a charm! If you see strange tiling artifacts, you’re likely not respecting the world->local transformation.

REMEMBER that no matter what if you are creating a new heightfield you should set every value to something known – the memory returned in a new heightfield is unitiialized and could contain junk values that WM will REALLY not like. If you’re not going to write to every location, just call Clear() on your heightfield before you start work on it to set everything to zero.

Thanks for the quick reply Remnant, that did shed some light on my problems!

Your second code snippet looks exactly like the thing I was looking for and I’ll put it to the test tomorrow morning asap…
I read and managed to compile your (well written) examples and already wrote a handy intersection filter (iterating over all the values as mentioned).
Was just a bit unsure how world machine managed the layout view (using various threads) and didn’t want to iterate over all the pixels when just creating some “arbitrary” data in some predefined space using world coordinates. The notion of having acces to the exact ws position together with the actual render context (area) is something I have to get used to

Again, thanks for the clarification. I assume there will be more questions but this should help me get further!

Cheers Coen

Got my test to work! Your feedback was exactly what I was looking for.
Here’s the bit of code that uses a specific list of world space coordinates to create height data, bound by the user specified range…

Thanks again for al the help! No on to some more interesting looking things :wink:

bool Scatter::Activate(BuildContext &context) {

	// Create a new heightfield using current specified world size
	// Make sure to set it's contents to a value of 0.0
	HField *map = GetNewHF(context.GetWorldSize());
	map->Clear();

	// get the current origin
	CoordF origin = getOrigin();	

	// get our parameters
	float scale = Scale;
	float area = Area;
	int seed = Seed;
	int generator_amount = Amount;

	// Create a Random Coordinate array
	// Array is constructed out of world space coordinates
	// Amount of entries is controlled through the amount parameter
	vector<CoordF> generators;
	srand(seed);
	for (int i=0; i<generator_amount; i++)
	{
		// This is where we create a world space coordinate based on a random value fit within the range, Specified by the user
		// The result is added on to the generators stack
		float xcor = (rand() % int(area*100.0f)/100.0f)-(area/2.0f)+(origin.x);
		float ycor = (rand() % int(area*100.0f)/100.0f)-(area/2.0f)+(origin.y);

		CoordF rint = CoordF(xcor,ycor);
		generators.push_back(rint);
	}

	// Create our height map bound information
	Rect4 bounds; 
	bounds.x0 = 0;
	bounds.y0 = 0;
	bounds.x1 = map->w()-1;
	bounds.y1 = map->h()-1;

	// Increment Our Seed
	srand(seed+1);

	// iterate over all our coordinates in the array
	// make sure the values are within range to output!
	// add random value if within height map tile
	for(vector<CoordF>::iterator i = generators.begin(); i!=generators.end(); ++i)
	{
		CoordF local_loc = TransformCoord_WorldToHF(context,*i);
		if (Intersect_Point_Rect4(Coord(int(local_loc.x),int(local_loc.y)), bounds)) 
		{
			(*map)[Coord(int(local_loc.x),int(local_loc.y))] = (float(rand() % 100)/100.0f) * scale;
		}
	}

	// Handoff to output(s)
	StoreData(map,0, context); // pass the heightfield to our output stage.
	// Success!
	return true;
}

On a quick side note, would it be possible to get the PDK compiled and working for Visual Studio 2010?
That would really help us out over here (Guerrilla), as we write all of our other code in vs2010 (+perforce integration)

Yep, I am actually moving the WM code over to MSVC2010 for WM 2.3. When that happens the PDK will be updated as well

Great news :slight_smile:
I’ll keep an eye on the forum

pnrqpxy@iuy.pw

adoyiqu@pop3.namnerbca.com

azqoixi@pop3.namnerbca.com