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.