So I've ported a terrain generator to SA:MP (a GTA:SA mod that connects you to players around the world). The terrain is generated perfectly, after only a few hours of scripting. I was surprised how simple it was.
The problem is that in SA:MP only 1000 objects max can be streamed in for a player at a time, and I've got it set to a 500 limit to ensure play quality. Obviously, streaming all cubes that are in the players' range (which is currently what I'm doing and it's horrible) would be very inefficient! I need an algorithm that would stream only the cubes that are on an outside layer (any objects that wouldn't be visible to the players view shouldn't be streamed).
The current streamer is located here.
The terrain generator is exactly as follows:
#include a_samp
#include streamer
#define START_X 5000.0
#define START_Y 5000.0
#define START_Z -5.5
#define WIDTH 128
#define HEIGHT 128
#define DEPTH 32
#define WATER_LEVEL 6
#define SEED 2512389
Float:findnoise(Float:x, Float:y)
{
new n = (floatround(x) + floatround(y) * 57);
n = ((n + SEED) << 13) ^ (n + SEED);
new nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
return 1.0 - (float(nn) / 1073741824.0);
}
Float:interpolate(Float:a, Float:b, Float:x)
{
new Float:ft = x * 3.1415927;
new Float:f = (1.0 - floatcos(ft)) * 0.5;
return a * (1.0 - f) + b * f;
}
Float:noise(Float:x, Float:y)
{
new Float:floorx = floatround(x, floatround_floor);
new Float:floory = floatround(y, floatround_floor);
new Float:s = findnoise(floorx, floory),
Float:t = findnoise(floorx + 1, floory),
Float:u = findnoise(floorx, floory + 1),
Float:v = findnoise(floorx + 1, floory + 1);
return interpolate(interpolate(s, t, x - floorx), interpolate(u, v, x - floorx), y - floory);
}
render(Float:zoom = 25.0, Float:p = 0.25)
{
new octaves = 2;
for(new y; y < HEIGHT; y++)
{
for(new x; x < WIDTH; x++)
{
new Float:getnoise;
for(new a; a < octaves - 1; a++)
{
new Float:frequency = floatpower(2.0, a);
new Float:amplitude = floatpower(p, a);
getnoise += noise(float(x) * frequency / zoom, float(y) / zoom * frequency) * amplitude;
}
new height = floatround((getnoise * 128.0) + 128.0);
clamp(height, 0, 255);
new ez = floatround(float(height * DEPTH) / 255.0);
//Sand //Grass //Dirt //Water //Stone
static colors[5] = {0xFFFFEB35, 0xFF573B0C, 0xFF5E9D34, 0xFF009999, 0xFF808080};
for(new z; z < ez + 1; z++)
{
new type = GetBlock(z, ez);
if(type)
{
new o = CreateDynamicObject(19789, x + START_X, y + START_Y, z + START_Z, 0.0, 0.0, 0.0);
SetDynamicObjectMaterial(o, 0, -1, "none", "none", colors[type - 1]);
}
}
}
}
}
GetBlock(z, height)
{
if (height > WATER_LEVEL)
{
if(z > height)
return 0;//Air
if(z == height)
return 3;//Grass
if(z > height - 7) //-7
return 2;//Dirt
}
else
{
if(z > height) //5
return 0;//Water - 4
if(z > height - 2) //-5
return 1;//Sand
if(z > height - 7)//-10
return 2;//Dirt
}
return 5;//Stone
}
main(){}
public OnGameModeInit()
{
render();
AddPlayerClass(0, START_X, START_Y, START_Z + 10.0, 0.0, 0, 0, 0, 0, 0, 0);
return 1;
}
The render
function is what begins the generation and creates the objects (cubes). OnGameModeInit
is a callback that is ran as soon as the server is initialized. Currently there isn't any textures applied, just some color based on the block type, but that's off topic.
Is it reasonable to edit the source of the streamer to only stream the visible layer? If so, is anyone willing to help me with it? If nobody is willing to help personally, can someone at least tell me how I should go about doing this?
Note: This was ruled off-topic on the Arqade SE.