Regions, Coord Grid the map? possible or not?

Is it possible to add regions that render within a certain distance of the character for ex: character is in scene1.j3o (region1), gets within a certain distance from a point in scene one (the edge) & it renders scene2.j3o(region2) right next to region1, while still rendering region1. (now rendering region 1 & 2 until the characters render distance no longer can see region1 (standing in region2) then it stops rendering region1.

In the end building 1 huge map that does not need to be rendered all at once. only in regions.

above would kind of help me tie these idea’s together.

Is it possible to do something like x/y coord on a scene/Map for ex:

red being 1,1 yellow being 2,2 green being 3,3. I’m looking to being able to add Player.setPlayerPosition(2,2); & it teleport or jump the character to that specific coordinate.

If anyone can help me out with this it will be greatly appreciated. Thanks in advance :stuck_out_tongue:

yes its quite easy, its just math, i wrote some simpleGgrid that calcs such stuff and then fires events when tile is loaded/unloaded su u can use it to load your scenes
if u will need help with this just pm me

Yeah, it’s called pagination - there are a few things available like terrain grid etc within jme to help. A few people have started working on frameworks to make it easier but I don’t think there are any complete solutions just yet.

The way minecraft does it is like this:

[java]
private List<Vector2f> getSurrounding(Vector2f chunkLoc)
{

// view distance var = amount of chunks we want to display in all directions.
// for example, a view distance of 3 would display a 7x7 area (3 chunks in all directions + the chunk you are standing in).

int topLx = toInt(chunkLoc.getX()) - viewDistance;
int topLz = toInt(chunkLoc.getY()) - viewDistance;

int botLx = toInt(chunkLoc.getX()) + viewDistance;
int botLz = toInt(chunkLoc.getY()) + viewDistance;

List&lt;Vector2f&gt; results = new ArrayList&lt;Vector2f&gt;();

for (int x = topLx; x &lt;= botLx; x++)
{
    for (int z = topLz; z &lt;= botLz; z++)
    {
        results.add(new Vector2f(x, z));
    }
}

return results;

}

[/java]

That method simply gives you the chunk locations around your player. Using that, you can work out the following:

[java]

// use the method above to calculate the chunks surrounding the player
// the “newChunkLoc” var is a Vector3f containing the chunk location of the player
// the “loadedChunk” var is a Vector2f ArrayList containing all loaded chunk co-ordinates.

List<Vector2f> surroundingChunkLocs = getSurrounding(newChunkLoc);

List<Vector2f> oldChunks = new ArrayList<Vector2f>(loadedChunks);
oldChunks.removeAll(surroundingChunkLocs);

List<Vector2f> newChunks = new ArrayList<Vector2f>(surroundingChunkLocs);
newChunks.removeAll(loadedChunks);

// if there are no new chunks, its because the loadedChunks arraylist was empty
// meaning the player just joined the game.
if (newChunks.isEmpty())
{
newChunks = surroundingChunkLocs;
}
[/java]

So now we have a list of the old chunks that we want to remove from the scene and a list of new chunks we want to add to the scene.
In the update method of the world or player, you would calculate whether the player had moved into a new chunk.
You can do this using bitshifting:

[java]
private int bitCalc(int blockSize)
{
switch (blockSize)
{
case 17: return 4;
case 33: return 5;
case 65: return 6;
case 129: return 7;
case 257: return 8;
case 513: return 9;
case 1024: return 10;
default: return 0;
}
}

int bitshiftVal = bitCalc(myBlockSize);

public static Vector2f WorldLocationToChunkLocation(Vector2f worldLocation)
{
int worldX = Math.round(worldLocation.getX());
int worldZ = Math.round(worldLocation.getY());

int chunkX = worldX &gt;&gt; bitshiftVal;
int chunkZ = worldZ &gt;&gt; bitshiftVal;

return new Vector2f(chunkX, chunkZ);

}
[/java]

So now, you can calculate if the player moved into a new chunk, and if so, which chunks you need to remove, and which chunks you need to load.
Below is a rough implementation of the above methods, using callables for good ol’ multi-threading.

[java]
public class TerrainGenerator extends TerrainQuad
{
private final GameContext context;

private final int viewDistance;

private final int bitshift;
private final Vector3f localScale;
private final List&lt;Vector2f&gt; loadedChunks;

private final HeightMapGenerator heightmapGen;
private final HumidityGenerator humidityGen;
private final TemperatureGenerator temperatureGen;

private final Plants trees;

public TerrainGenerator(GameContext context)
{
    this.context = context;
    this.loadedChunks = new CopyOnWriteArrayList&lt;Vector2f&gt;();

    this.viewDistance = 4;
    this.bitshift =  bitCalc(Game.BLOCK_SIZE);
    this.localScale = new Vector3f(1f, 1f, 1f);

    this.trees = new Plants(context);

    this.heightmapGen = new HeightMapGenerator();
    this.humidityGen = new HumidityGenerator();
    this.temperatureGen = new TemperatureGenerator();

    this.lastChunkLoc = new Vector2f(Float.MAX_VALUE, Float.MAX_VALUE);
}

private int bitCalc(int blockSize)
{
    switch (blockSize)
    {
        case 17: return 4;
        case 33: return 5;
        case 65: return 6;
        case 129: return 7;
        case 257: return 8;
        case 513: return 9;
        case 1024: return 10;
        default: return 0;
    }
}

private void loadVehicle()
{
    // wait until we have some terrain before loading the car.
    if (loadedChunks.isEmpty() == false &amp;&amp; context.getVehicle() == null)
    {
        Vehicle vehicle =
                // new Ford_Bronco(context);
                new Lincoln_Navigator(context);
        context.setVehicle(vehicle);

        context.getGame().getRootNode().attachChild(vehicle.getVehicleNode());
        vehicle.getVehicleControl().setPhysicsLocation(new Vector3f(5, 97, -15));
        context.getGame().getPhysicsSpace().add(vehicle.getVehicleControl());

        vehicle.initLights();

        vehicle.setVehicleParked(true);


    }
}

private void loadPlayer()
{
    if (loadedChunks.isEmpty() == false &amp;&amp; context.getPlayer() == null)
    {
        Player player = new Player(context);
        context.setPlayer(player);

        context.getGame().getRootNode().attachChild(player.getPlayerNode());
        player.getCharacterControl().setPhysicsLocation(new Vector3f(0, 92, 0));
        context.getGame().getPhysicsSpace().add(player.getCharacterControl());

        player.registerActionListener();

        context.getGame().setupCamera();
    }
}

public void simpleUpdate(float tpf)
{
    float actualX;
    float actualZ;

    // float actualX = this.context.getVehicle().getVehicleNode().getLocalTranslation().getX() + Game.TILE_SIZE;
    // float actualZ = this.context.getVehicle().getVehicleNode().getLocalTranslation().getZ() + Game.TILE_SIZE;



    actualX = this.context.getGame().getCamera().getLocation().getX() + Game.TILE_SIZE;
    actualZ = this.context.getGame().getCamera().getLocation().getZ() + Game.TILE_SIZE;

    pointX = toChunkCoord(actualX);
    pointZ = toChunkCoord(actualZ);

    Vector2f chunk = new Vector2f(pointX, pointZ);

    // if player is in the same location, ignore
    if (chunk.equals(lastChunkLoc) == false)
    {
        newChunkLoc = chunk.clone();
        // new chunks
        if (newChunksFuture == null)
        {
            newChunksFuture = context.getGame().getExecutor().submit(loadNewTerrain);
        }
        else
        {
            if (newChunksFuture.isDone())
            {
                try
                {
                    ChunkState newState = (ChunkState)newChunksFuture.get();

                    // remove old
                    for (int i = 0; i &lt; newState.getOldChunks().size(); i++)
                    {
                        Vector2f thisChunkLoc = newState.getOldChunks().get(i);

                        int chunkX = toLocationCoord(thisChunkLoc.getX());
                        int chunkZ = toLocationCoord(thisChunkLoc.getY());

                        String friendlyName = "chunk" + "_" + chunkX + "_" + chunkZ;

                        final TerrainQuad quad = (TerrainQuad)context.getGame().getRootNode().getChild(friendlyName);

                        context.getGame().getRootNode().detachChild(quad);
                        context.getGame().getPhysicsSpace().remove(quad);
                        loadedChunks.remove(thisChunkLoc);

                    }

                    // add new
                    for (int i = 0; i &lt; newState.getNewChunks().size(); i++)
                    {
                        Chunk cnk = newState.getNewChunks().get(i);

                        context.getGame().getRootNode().attachChild(cnk);
                        context.getGame().getPhysicsSpace().add(cnk);
                        cnk.setBatchHint(BatchHint.Always);
                        // grass(cnk);
                    }


                    loadPlayer();
                    loadVehicle();

                    // finally
                    lastChunkLoc = newChunkLoc.clone();
                    newChunksFuture = null;

                }
                catch (Exception ex) { }
            }
        }
    }
}

private int pointX;
private int pointZ;
private Vector2f newChunkLoc = null;
private Vector2f lastChunkLoc = null;
// private List&lt;Vector2f&gt; newChunks = new ArrayList&lt;Vector2f&gt;();

private Future newChunksFuture;
// private Future oldChunksFuture;

private Callable&lt;ChunkState&gt; loadNewTerrain = new Callable&lt;ChunkState&gt;()
{
    public ChunkState call()
    {
        List&lt;Chunk&gt; chunksToAdd = new ArrayList&lt;Chunk&gt;();

        List&lt;Vector2f&gt; surroundingChunkLocs = getSurrounding(newChunkLoc);

        List&lt;Vector2f&gt; oldChunks = new ArrayList&lt;Vector2f&gt;(loadedChunks);
        oldChunks.removeAll(surroundingChunkLocs);

        List&lt;Vector2f&gt; newChunks = new ArrayList&lt;Vector2f&gt;(surroundingChunkLocs);
        newChunks.removeAll(loadedChunks);

        if (newChunks.isEmpty())
        {
            newChunks = surroundingChunkLocs;
        }

        for (int i = 0; i &lt; newChunks.size(); i++)
        {
            Vector2f thisChunkLoc = newChunks.get(i);

            int chunkX = ((Number)thisChunkLoc.getX()).intValue() &lt;&lt; bitshift;
            int chunkZ = ((Number)thisChunkLoc.getY()).intValue() &lt;&lt; bitshift;

            String friendlyName = "chunk" + "_" + chunkX + "_" + chunkZ;

            loadedChunks.add(thisChunkLoc);

            int imgChunkX = Math.round(thisChunkLoc.getX());
            int imgChunkZ = Math.round(thisChunkLoc.getY());

            float[] heightMap = heightmapGen.get(imgChunkX, imgChunkZ);
            // float[] humidityMap = humidityGen.get(imgChunkX, imgChunkZ);
            float[] temperatureMap = temperatureGen.get(imgChunkX, imgChunkZ);

            // post processor for biomes
            // temperature map affects textures
            //
            // float[] newHeightMap = Game.game.sandGenerator.process(imgChunkX, imgChunkZ, temperatureMap, heightMap);

            Chunk quad = new Chunk(friendlyName, Game.TILE_SIZE, Game.BLOCK_SIZE, heightMap, temperatureMap, imgChunkX, imgChunkZ);

            Image alphaImg = quad.getAlphaMap();
            Texture alphaTex = new Texture2D(alphaImg);

            // ByteBuffer pos_y = alphaImg.getData(2);
            // ByteBuffer p2 = alphaImg.getData(3);
            // alphaImg.setData(2, alphaImg.getData(3));
            // alphaImg.setData(3, pos_y);

            Material terrainMat = new Material(context.getGame().getAssetManager(), "Common/MatDefs/Terrain/TerrainLighting.j3md");
            terrainMat.setBoolean("WardIso", false);
            terrainMat.setFloat("Shininess", 0.2f);
            terrainMat.setTexture("AlphaMap", alphaTex);


            // TextureKey tk = new TextureKey("Textures/Terrain/Desert.jpg", false);


            // load grass texture
            Texture grass = context.getGame().getAssetManager().loadTexture("Textures/Terrain/Shrubland.jpg");
            grass.setWrap(WrapMode.Repeat);
            terrainMat.setTexture("DiffuseMap", grass);
            terrainMat.setFloat("DiffuseMap_0_scale", 8f);

            // load dirt texture
            Texture dirt = context.getGame().getAssetManager().loadTexture("Textures/Terrain/Tundra.jpg");
            dirt.setWrap(WrapMode.Repeat);
            terrainMat.setTexture("DiffuseMap_1", dirt);
            terrainMat.setFloat("DiffuseMap_1_scale", 8f);

            // load rock texture
            Texture rock = context.getGame().getAssetManager().loadTexture("Textures/Terrain/Snow.jpg");
            rock.setWrap(WrapMode.Repeat);
            terrainMat.setTexture("DiffuseMap_2", rock);
            terrainMat.setFloat("DiffuseMap_2_scale", 8f);

            quad.setMaterial(terrainMat);

            quad.setLocalTranslation(chunkX, 0, chunkZ);
            quad.addControl(new RigidBodyControl(new HeightfieldCollisionShape(quad.getHeightMap(), localScale), 0));

            TerrainLodControl control = new TerrainLodControl(quad, context.getGame().getCamera());
            control.setLodCalculator( new DistanceLodCalculator(Game.TILE_SIZE, 2.0f) ); // patch size, and a multiplier
            quad.addControl(control);

            quad.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);

            // trees
            Random rnd = new Random();

            for (int t = 0; t &lt; rnd.nextInt(4); t++)
            {
                // genTree(quad);
            }

            for (int t = 0; t &lt; rnd.nextInt(25); t++)
            {
                // genGrass(quad);
                // genCloud(quad);
            }

            // grass
            // Node grassNode = Grass.createPatchField(parent, context.getGame().getAssetManager(), "Textures/Terrain/Grass.jpg", 1.0f, 10f, 1.0f, 1.0f, 30f, 3.0f, 1.0f, 1, 4);

            // quad.attachChild(grassNode);

            chunksToAdd.add(quad);

        }

        return new ChunkState(chunksToAdd, oldChunks);
    }
};


public int toInt(float value) { return ((Number)value).intValue(); }
public int toChunkCoord(float axis) { return ((Number)axis).intValue() &gt;&gt; this.bitshift; }
public int toLocationCoord(float axis) { return ((Number)axis).intValue() &lt;&lt; this.bitshift; }

private List&lt;Vector2f&gt; getSurrounding(Vector2f chunkLoc)
{
    int topLx = toInt(chunkLoc.getX()) - viewDistance;
    int topLz = toInt(chunkLoc.getY()) - viewDistance;

    int botLx = toInt(chunkLoc.getX()) + viewDistance;
    int botLz = toInt(chunkLoc.getY()) + viewDistance;

    List&lt;Vector2f&gt; results = new ArrayList&lt;Vector2f&gt;();

    for (int x = topLx; x &lt;= botLx; x++)
    {
        for (int z = topLz; z &lt;= botLz; z++)
        {
            results.add(new Vector2f(x, z));
        }
    }

    return results;
}

}
[/java]

And finally, a chunkstate is just a tuple really. I just like things to be nice and orderly.

[java]
public final class ChunkState
{
private final List<Chunk> newChunks;
private final List<Vector2f> oldChunks;

public ChunkState(List&lt;Chunk&gt; newChunks, List&lt;Vector2f&gt; oldChunks)
{
    this.newChunks = newChunks;
    this.oldChunks = oldChunks;
}

public List&lt;Chunk&gt; getNewChunks() { return this.newChunks; }
public List&lt;Vector2f&gt; getOldChunks() { return this.oldChunks; }

}
[/java]

Hope that helps :slight_smile: