SlopeMap access through Lua API
Moderator: Moderators
SlopeMap access through Lua API
Hi!
I've been searching through Lua API documentation and noticed there is no explicit function to return spring slopeMap. Spring.GetGroundNormal exists but my (limited) dig into springrts code made me think that it references some other kind of Normals, than ones that slope map is based on.
Exposure of the Slope Map could be useful for a gadget/widget author in case own pathfinding algorithm is developed or in case there is a need to calculate an effective unit's speed at particular point on the ground.
Am I missing something or could this be considered as an improvement to Lua AI?
I've been searching through Lua API documentation and noticed there is no explicit function to return spring slopeMap. Spring.GetGroundNormal exists but my (limited) dig into springrts code made me think that it references some other kind of Normals, than ones that slope map is based on.
Exposure of the Slope Map could be useful for a gadget/widget author in case own pathfinding algorithm is developed or in case there is a need to calculate an effective unit's speed at particular point on the ground.
Am I missing something or could this be considered as an improvement to Lua AI?
-
- Moderator
- Posts: 2464
- Joined: 12 Oct 2007, 09:24
Re: SlopeMap access through Lua API
Spring.GetGroundNormal is the function you want.
Re: SlopeMap access through Lua API
Are you sure about that?Google_Frog wrote:Spring.GetGroundNormal is the function you want.
The code of getting GetGroundNormal values eventually converge to centerNormalsSynced/centerNormalsUnsynced arrays, while slopeMap is calculated using faceNormalsSynced array. Also code for getting two arrays looks very different and only slightly related.
-
- Moderator
- Posts: 2464
- Joined: 12 Oct 2007, 09:24
Re: SlopeMap access through Lua API
Yes.Are you sure about that?
You would get further if you did stuff.
Re: SlopeMap access through Lua API
I've just "done stuff", specifically my own Spring.GetGroundSlope implementation, and found out that slope and normal components values are different.Google_Frog wrote:Yes.Are you sure about that?
You would get further if you did stuff.
if you know what math to do to transform nx, ny, nz=Spring.GetGroundNormal(x,z) to slope value, don't hesitate to enlight me.
Re: SlopeMap access through Lua API
see the GetSlopeMapImage function in http://springfiles.com/spring/lua-scrip ... eightmap-0
just use the Y normal value (why this works is beyond me, i don't understand what a normal is)
just use the Y normal value (why this works is beyond me, i don't understand what a normal is)
Re: SlopeMap access through Lua API
A normal vector stands at right angle to other things.
Spring.GetGroundNormal returns a vector that stands at right angle on the ground.
So on flat terrain it points straight to the sky: x=0,y=1,z=0
If the terrain is tilted, so is the vector.
The steeper the terrain, the lower the y-componente becomes and x,z become larger.
On side of a vertical wall y would be zero, but spring does not have truely vertical walls, so it just gets closer to zero the steeper the slope gets.
If the slope-direction does not matter and one is only interessted in slope-slope (like http://springfiles.com/spring/lua-scrip ... eightmap-0 ) then it is enough to use only the y-component of the normal-vector:
y=1 means completly flat, y=0 means vertical.
How that relates to spring's pathing is explained here:
https://springrts.com/wiki/Movedefs.lua ... determined
Spring.GetGroundNormal returns a vector that stands at right angle on the ground.
So on flat terrain it points straight to the sky: x=0,y=1,z=0
If the terrain is tilted, so is the vector.
The steeper the terrain, the lower the y-componente becomes and x,z become larger.
On side of a vertical wall y would be zero, but spring does not have truely vertical walls, so it just gets closer to zero the steeper the slope gets.
If the slope-direction does not matter and one is only interessted in slope-slope (like http://springfiles.com/spring/lua-scrip ... eightmap-0 ) then it is enough to use only the y-component of the normal-vector:
y=1 means completly flat, y=0 means vertical.
How that relates to spring's pathing is explained here:
https://springrts.com/wiki/Movedefs.lua ... determined
Re: SlopeMap access through Lua API
As I wrote in my last post, in the meantime I've run few experiments.
Despite believe Google_Frog and others may have, that
it's not quite the case strictly. Here is the visual example:
http://snag.gy/eDbEi.jpg
I've also dumped whole array of Y_normals and slopeMap and found out, that while the sum of slope(taken from engine) and yn in most cases is close to 1.0, there are cases where sum goes as high as 1.722975 and as low as 0.676002 (map Zion_v1)
So if you don't mind, I'd like to propose the following patch, which adds support of reading slopeMap
Despite believe Google_Frog and others may have, that
Code: Select all
local _,yn=Spring.GetGroundNormal(x,z)
local slope=1-yn
http://snag.gy/eDbEi.jpg
I've also dumped whole array of Y_normals and slopeMap and found out, that while the sum of slope(taken from engine) and yn in most cases is close to 1.0, there are cases where sum goes as high as 1.722975 and as low as 0.676002 (map Zion_v1)
This description is right, but it doesn't say there is a number of normal arrays, as I mentioned in OP. Apparently they are calculated slightly differently.How that relates to spring's pathing is explained here:
https://springrts.com/wiki/Movedefs.lua ... determined
So if you don't mind, I'd like to propose the following patch, which adds support of reading slopeMap
Code: Select all
diff --git a/rts/Lua/LuaSyncedRead.cpp b/rts/Lua/LuaSyncedRead.cpp
index f10cd2f..ec93209 100644
--- a/rts/Lua/LuaSyncedRead.cpp
+++ b/rts/Lua/LuaSyncedRead.cpp
@@ -289,6 +289,7 @@ bool LuaSyncedRead::PushEntries(lua_State* L)
REGISTER_LUA_CFUNC(GetGroundHeight);
REGISTER_LUA_CFUNC(GetGroundOrigHeight);
REGISTER_LUA_CFUNC(GetGroundNormal);
+ REGISTER_LUA_CFUNC(GetGroundSlope);
REGISTER_LUA_CFUNC(GetGroundInfo);
REGISTER_LUA_CFUNC(GetGroundBlocked);
REGISTER_LUA_CFUNC(GetGroundExtremes);
@@ -4739,6 +4740,13 @@ int LuaSyncedRead::GetGroundNormal(lua_State* L)
return 3;
}
+int LuaSyncedRead::GetGroundSlope(lua_State* L)
+{
+ const float x = luaL_checkfloat(L, 1);
+ const float z = luaL_checkfloat(L, 2);
+ lua_pushnumber(L, CGround::GetSlope(x, z, CLuaHandle::GetHandleSynced(L)));
+ return 1;
+}
int LuaSyncedRead::GetGroundInfo(lua_State* L)
{
diff --git a/rts/Lua/LuaSyncedRead.h b/rts/Lua/LuaSyncedRead.h
index b665959..eccbcda 100644
--- a/rts/Lua/LuaSyncedRead.h
+++ b/rts/Lua/LuaSyncedRead.h
@@ -199,6 +199,7 @@ class LuaSyncedRead {
static int GetGroundHeight(lua_State* L);
static int GetGroundOrigHeight(lua_State* L);
static int GetGroundNormal(lua_State* L);
+ static int GetGroundSlope(lua_State* L);
static int GetGroundInfo(lua_State* L);
static int GetGroundBlocked(lua_State* L);
static int GetGroundExtremes(lua_State* L);
Re: SlopeMap access through Lua API
Thanks for the patch, even better if a pull request on github
Re: SlopeMap access through Lua API
Your passionate speech made me learn basics of github & git. Here it's https://github.com/spring/spring/pull/173FLOZi wrote:Thanks for the patch, even better if a pull request on github
Re: SlopeMap access through Lua API
Most stuff in wiki/forum is about mod making point of view. For example that text is just about explaining that the numbers for maxSlope of a unit are not in degrees° and how they are to be understood.ivand wrote:This description is right, but it doesn't say there is a number of normal arrays, as I mentioned in OP. Apparently they are calculated slightly differently.How that relates to spring's pathing is explained here:
https://springrts.com/wiki/Movedefs.lua ... determined
For that it is not nessecary to know details like how many different arrays exist in engine etc.
This widget shows returned values of GetGroundNormal() and length of vector when a marker is placed. Did not check it in detail but the length is always 1 so seems correct. (floating point inaccury aside)
Code: Select all
function widget:MapDrawCmd(playerID, cmdType, px, py, pz, label)
if cmdType ~= "point" then return end
if label ~= "" then return end
local nx,ny,nz = Spring.GetGroundNormal (px,pz)
local length = math.sqrt(nx*nx + ny*ny + nz*nz)
Spring.MarkerAddPoint (px,py,pz, ("nx="..nx.." ny="..ny.." nz="..nz.." length="..length))
end
Why is nz==1.00 there?
If there is something not matching up then that would be interessting. But it is not quite clear to me what excactly you compared and how/what you read from engine? (which array/function etc)
Re: SlopeMap access through Lua API
@8611,
Good spot. There was a mistake, where rounding was applied to nx and nz components of Normal erroneously, it didn't apply to ny, though. So what I wrote yesterday still holds.
Here is relevant part of widget code. Quick'n'dirty style. Also it shows the part of code there aforementioned bug was.
Note Spring.GetGroundSlope requires modifications to the springrts engine.
The function I use to get slope value looks following:
Good spot. There was a mistake, where rounding was applied to nx and nz components of Normal erroneously, it didn't apply to ny, though. So what I wrote yesterday still holds.
Here is relevant part of widget code. Quick'n'dirty style. Also it shows the part of code there aforementioned bug was.
Code: Select all
function widget:MousePress(mx, my, button)
if not Spring.IsAboveMiniMap(mx, my) and button==1 then
local _, pos = Spring.TraceScreenRay(mx, my, true)
if pos then
local nx, ny, nz=Spring.GetGroundNormal(pos[1], pos[3])
--local nx=math.round(nx) <------------mistake
--local nz=math.round(nz) <------------mistake
--local n=math.sqrt(nx*nx+ny*ny+nz*nz)
local slope=Spring.GetGroundSlope(pos[1], pos[3])
--local str=string.format("nx=%.2f ny=%.2f nz=%.2f n=%.2f sl=%.2f", nx, ny, nz, n, slope)
local str=string.format("nx=%.2f ny=%.2f nz=%.2f sl=%.2f", nx, ny, nz, slope)
Spring.MarkerAddPoint(pos[1], 0, pos[3], str, true)
end
end
end
Engine's movement math is centered around slopeMap arrayIf there is something not matching up then that would be interessting. But it is not quite clear to me what excactly you compared and how/what you read from engine? (which array/function etc)
Code: Select all
const float slope = readMap->GetSlopeMapSynced()[square];
.......
const float* GetSlopeMapSynced() const { return &slopeMap[0]; }
Code: Select all
float CGround::GetSlope(float x, float z, bool synced)
{
const int xhsquare = Clamp(int(x) / (2 * SQUARE_SIZE), 0, mapDims.hmapx - 1);
const int zhsquare = Clamp(int(z) / (2 * SQUARE_SIZE), 0, mapDims.hmapy - 1);
const float* slopeMap = readMap->GetSharedSlopeMap(synced);
return slopeMap[xhsquare + zhsquare * mapDims.hmapx];
}
.......
const float* GetSharedSlopeMap(bool synced) const { return sharedSlopeMaps[synced]; }
.......
sharedSlopeMaps[0] = &slopeMap[0]; // NO UNSYNCED VARIANT
sharedSlopeMaps[1] = &slopeMap[0];
Re: SlopeMap access through Lua API
https://github.com/spring/spring/pull/1 ... 638fcR4748
Your new Spring.GetGroundSlope uses
CGround::GetSlope(x, z, CLuaHandle::GetHandleSynced(L))
https://github.com/lhog/spring/blob/dev ... .cpp#L4736
Spring.GetGroundNormal uses
normal = CGround::GetSmoothNormal(x, z, CLuaHandle::GetHandleSynced(L))
I have no idea which one is more correct...from quick look yours seems better to 'get slope like the engine uses it:'
Engine seems to use GetSlope() and GetNormal() in many places that seem physics/pathing related.
GetSmoothNormal() seems used in camera stuff and the parts where it is used in physics seem less relevant.
Another guess is that the x,z coordinates somehow get snapped/rounded to the heightmap square's resolution and so the functions are not reading the same coordinates.
But really no idea of those things, someone who knows better will probally reply to your PR anyway
/edit: hm did you just edit post? Anyway, seems you know more than me there so "good luck!" :)
Your new Spring.GetGroundSlope uses
CGround::GetSlope(x, z, CLuaHandle::GetHandleSynced(L))
https://github.com/lhog/spring/blob/dev ... .cpp#L4736
Spring.GetGroundNormal uses
normal = CGround::GetSmoothNormal(x, z, CLuaHandle::GetHandleSynced(L))
I have no idea which one is more correct...from quick look yours seems better to 'get slope like the engine uses it:'
Engine seems to use GetSlope() and GetNormal() in many places that seem physics/pathing related.
GetSmoothNormal() seems used in camera stuff and the parts where it is used in physics seem less relevant.
Another guess is that the x,z coordinates somehow get snapped/rounded to the heightmap square's resolution and so the functions are not reading the same coordinates.
But really no idea of those things, someone who knows better will probally reply to your PR anyway
/edit: hm did you just edit post? Anyway, seems you know more than me there so "good luck!" :)
Re: SlopeMap access through Lua API
Hey 8611.
Yeah, I've edited post multiple times to support the message better. Sorry about that
The whole point of topic as it turns out, that engine has different flavors of normals, which are calculated a little bit differently.
So if it's somehow related to rounding, then it's an engine side thing, not widget side.
Yeah, I've edited post multiple times to support the message better. Sorry about that
The whole point of topic as it turns out, that engine has different flavors of normals, which are calculated a little bit differently.
Thought so as well, that's why I made that silly mistake in the widget (rounding was supposed to be applied to coordinates not to normal's values). Anyway, when I dumped slopeMap and Y-component of GetGroundNormal for the whole map, I used this integer based code:Another guess is that the x,z coordinates somehow get snapped/rounded to the heightmap square's resolution and so the functions are not reading the same coordinates.
Code: Select all
local ynormalMap = assert(io.open("ynormalMap.txt", "w"))
for z=0, Game.mapSizeZ, 1 do
for x=0, Game.mapSizeX, 1 do
local _,val=Spring.GetGroundNormal(x, z)
--local vals=string.format("%.2f\n", val)
local vals=string.format("%f\n", val)
ynormalMap:write(vals)
end
end
assert(ynormalMap:close())
local slopeMap = assert(io.open("slopeMap.txt", "w"))
for z=0, Game.mapSizeZ, 1 do
for x=0, Game.mapSizeX, 1 do
local val=Spring.GetGroundSlope(x, z)
--local vals=string.format("%.2f\n", val)
local vals=string.format("%f\n", val)
slopeMap:write(vals)
end
end
assert(slopeMap:close())
local sumMap = assert(io.open("sumMap.txt", "w"))
for z=0, Game.mapSizeZ, 1 do
for x=0, Game.mapSizeX, 1 do
local slope=Spring.GetGroundSlope(x, z)
local _,ynorm=Spring.GetGroundNormal(x, z)
local val=ynorm+slope
--local vals=string.format("%.2f\n", val)
local vals=string.format("%f\n", val)
sumMap:write(vals)
end
end
assert(sumMap:close())
Re: SlopeMap access through Lua API
Spring.GetGroundNormal returns a vector that is interpolated between four *center* normals.
Each center (quad) normal is simply two face (triangle) normals averaged together, while the slopemap is constructed from *face* normals.
For that reason, adding...
... would also be a good idea.
Each center (quad) normal is simply two face (triangle) normals averaged together, while the slopemap is constructed from *face* normals.
For that reason, adding...
Code: Select all
diff --git a/rts/Lua/LuaSyncedRead.cpp b/rts/Lua/LuaSyncedRead.cpp
index f10cd2f..0716e01 100644
--- a/rts/Lua/LuaSyncedRead.cpp
+++ b/rts/Lua/LuaSyncedRead.cpp
@@ -4732,7 +4732,12 @@ int LuaSyncedRead::GetGroundNormal(lua_State* L)
{
const float x = luaL_checkfloat(L, 1);
const float z = luaL_checkfloat(L, 2);
- const float3 normal = CGround::GetSmoothNormal(x, z, CLuaHandle::GetHandleSynced(L));
+
+ // smoothed or raw center-normal
+ const float3 normal = luaL_checkboolean(L, 3)?
+ CGround::GetNormal(x, z, CLuaHandle::GetHandleSynced(L)):
+ CGround::GetSmoothNormal(x, z, CLuaHandle::GetHandleSynced(L));
+
lua_pushnumber(L, normal.x);
lua_pushnumber(L, normal.y);
lua_pushnumber(L, normal.z);
Re: SlopeMap access through Lua API
@Kloot, Code blocks for getting CGround::GetNormal and CGround::GetSlope look related, but still a bit different:
Is it worth checking that slopeMap[x,z] is indeed easy to get from CGround::GetNormal[x,z](y component)?
Also does it make sense to Lua export these two functions (again for pathfinding & ETA)?
Code: Select all
const float3& CGround::GetNormal(float x, float z, bool synced)
....
const float3* normalMap = readMap->GetSharedCenterNormals(synced);
....
const float3* GetSharedCenterNormals(bool synced) const { return sharedCenterNormals[synced]; }
....
sharedCenterNormals[0] = ¢erNormalsUnsynced[0];
sharedCenterNormals[1] = ¢erNormalsSynced[0];
.....
centerNormalsSynced[y * mapDims.mapx + x] = (fnTL + fnBR).Normalize();
Code: Select all
float CGround::GetSlope(float x, float z, bool synced)
....
const float* slopeMap = readMap->GetSharedSlopeMap(synced);
....
const float* GetSharedSlopeMap(bool synced) const { return sharedSlopeMaps[synced]; }
.....
sharedSlopeMaps[0] = &slopeMap[0]; // NO UNSYNCED VARIANT
sharedSlopeMaps[1] = &slopeMap[0];
.....
faceNormalsSynced[(y * mapDims.mapx + x) * 2 ] = fnTL;
faceNormalsSynced[(y * mapDims.mapx + x) * 2 + 1] = fnBR;
.....
float avgslope = 0.0f;
avgslope += faceNormalsSynced[(idx0 ) * 2 ].y;
avgslope += faceNormalsSynced[(idx0 ) * 2 + 1].y;
avgslope += faceNormalsSynced[(idx0 + 1) * 2 ].y;
avgslope += faceNormalsSynced[(idx0 + 1) * 2 + 1].y;
avgslope += faceNormalsSynced[(idx1 ) * 2 ].y;
avgslope += faceNormalsSynced[(idx1 ) * 2 + 1].y;
avgslope += faceNormalsSynced[(idx1 + 1) * 2 ].y;
avgslope += faceNormalsSynced[(idx1 + 1) * 2 + 1].y;
avgslope *= 0.125f;
float maxslope = faceNormalsSynced[(idx0 ) * 2 ].y;
maxslope = std::min(maxslope, faceNormalsSynced[(idx0 ) * 2 + 1].y);
maxslope = std::min(maxslope, faceNormalsSynced[(idx0 + 1) * 2 ].y);
maxslope = std::min(maxslope, faceNormalsSynced[(idx0 + 1) * 2 + 1].y);
maxslope = std::min(maxslope, faceNormalsSynced[(idx1 ) * 2 ].y);
maxslope = std::min(maxslope, faceNormalsSynced[(idx1 ) * 2 + 1].y);
maxslope = std::min(maxslope, faceNormalsSynced[(idx1 + 1) * 2 ].y);
maxslope = std::min(maxslope, faceNormalsSynced[(idx1 + 1) * 2 + 1].y);
// smooth it a bit, so small holes don't block huge tanks
const float lerp = maxslope / avgslope;
const float slope = mix(maxslope, avgslope, lerp);
Also does it make sense to Lua export these two functions (again for pathfinding & ETA)?
Code: Select all
float CMoveMath::GetPosSpeedMod(const MoveDef& moveDef, int xSquare, int zSquare)
float CMoveMath::GetPosSpeedMod(const MoveDef& moveDef, int xSquare, int zSquare, const float3& moveDir)