Index: ps/trunk/binaries/data/mods/public/maps/random/rmgen/mapgen.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/rmgen/mapgen.js (revision 10877) +++ ps/trunk/binaries/data/mods/public/maps/random/rmgen/mapgen.js (revision 10878) @@ -1,79 +1,81 @@ +var TILE_CENTERED_HEIGHT_MAP = false; + var g_Map; var g_Environment = { SkySet: "default", SunColour: {r: 0.749020, g: 0.749020, b: 0.749020, a: 0}, SunElevation: 0.785398, SunRotation: 5.49779, TerrainAmbientColour: {r: 0.501961, g: 0.501961, b: 0.501961, a: 0}, UnitsAmbientColour: {r: 0.501961, g: 0.501961, b: 0.501961, a: 0}, Water: { WaterBody: { Type: "default", Colour: {r: 0.3, g: 0.35, b: 0.7, a: 0}, Height: 5, Shininess: 150, Waviness: 8, Murkiness: 0.45, Tint: {r: 0.28, g: 0.3, b: 0.59, a: 0}, ReflectionTint: {r: 0.28, g: 0.3, b: 0.59, a: 0}, ReflectionTintStrength: 0.0 } } }; var g_Camera = { Position: {x: 100, y: 150, z: -100}, Rotation: 0, Declination: 0.523599 }; var g_CivData = {}; ///////////////////////////////////////////////////////////////////////////////////// function InitMap() { if (g_MapSettings === undefined) { // Should never get this far, failed settings would abort prior to loading scripts throw("InitMapGen: settings missing"); } // Get civ data as array of JSON strings var data = RMS.GetCivData(); if (!data || !data.length) { throw("InitMapGen: error reading civ data"); } for (var i = 0; i < data.length; ++i) { var civData = JSON.parse(data[i]); g_CivData[civData.Code] = civData; } // Create new map log("Creating new map..."); var terrain = createTerrain(g_MapSettings.BaseTerrain); g_Map = new Map(g_MapSettings.Size, g_MapSettings.BaseHeight); g_Map.initTerrain(terrain); } function ExportMap() { // Wrapper for engine function log("Saving map..."); // Get necessary data from map var data = g_Map.getMapData(); // Add environment and camera settings g_Environment.Water.WaterBody.Height = SEA_LEVEL - 0.1; data.Environment = g_Environment; // Adjust default cam to roughly center of the map - useful for Atlas g_Camera.Position = {x: g_MapSettings.Size*2, y: g_MapSettings.Size*2, z: -g_MapSettings.Size*2}; data.Camera = g_Camera; RMS.ExportMap(data); } Index: ps/trunk/binaries/data/mods/public/maps/random/rmgen/map.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/rmgen/map.js (revision 10877) +++ ps/trunk/binaries/data/mods/public/maps/random/rmgen/map.js (revision 10878) @@ -1,349 +1,395 @@ ////////////////////////////////////////////////////////////////////// // Map // // Class for holding map data and providing basic API to change it // // size: Size of the map in tiles // baseHeight: Starting height of the map // ////////////////////////////////////////////////////////////////////// function Map(size, baseHeight) { // Size must be 0 to 1024, divisible by patches this.size = size; // Create 2D arrays for textures, object, and areas this.texture = new Array(size); this.terrainObjects = new Array(size); this.area = new Array(size); for (var i = 0; i < size; i++) { this.texture[i] = new Uint16Array(size); // uint16 - texture IDs this.terrainObjects[i] = new Array(size); // array of entities this.area[i] = new Uint16Array(size); // uint16 - area IDs for (var j = 0; j < size; j++) { this.terrainObjects[i][j] = []; } } // Create 2D array for heightmap - var mapSize = size+1; + var mapSize; + if (TILE_CENTERED_HEIGHT_MAP) + { + mapSize = size; + } + else + { + mapSize = size+1; + } + this.height = new Array(mapSize); for (var i = 0; i < mapSize; i++) { this.height[i] = new Float32Array(mapSize); // float32 for (var j = 0; j < mapSize; j++) { // Initialize height map to baseHeight this.height[i][j] = baseHeight; } } // Create name <-> id maps for textures this.nameToID = {}; this.IDToName = []; //string // Other arrays this.objects = []; //object this.tileClasses = []; //int this.areaID = 0; // Starting entity ID this.entityCount = 150; } Map.prototype.initTerrain = function(baseTerrain) { // Initialize base terrain var size = this.size; for (var i = 0; i < size; i++) { for (var j = 0; j < size; j++) { baseTerrain.place(i, j); } } }; // Return ID of texture (by name) Map.prototype.getTextureID = function(texture) { if (texture in (this.nameToID)) { return this.nameToID[texture]; } // Add new texture var id = this.IDToName.length; this.nameToID[texture] = id; this.IDToName[id] = texture; return id; }; // Return next free entity ID Map.prototype.getEntityID = function() { return this.entityCount++; } // Check bounds on tile map Map.prototype.validT = function(x, z) { if (g_MapSettings.CircularMap) { // Within map circle var halfSize = Math.floor(0.5*this.size); var dx = (x - halfSize); var dz = (z - halfSize); return Math.round(Math.sqrt(dx*dx + dz*dz)) < halfSize; } else { // Within map square return x >= 0 && z >= 0 && x < this.size && z < this.size; } }; -// Check bounds on height map (size + 1 by size + 1) +// Check bounds on height map if TILE_CENTERED_HEIGHT_MAP==false then this is (size + 1 by size + 1) otherwise (size, size) Map.prototype.validH = function(x, z) { - return x >= 0 && z >= 0 && x <= this.size && z <= this.size; + if (TILE_CENTERED_HEIGHT_MAP) + { + return x >= 0 && z >= 0 && x < this.size && z < this.size; + } + else + { + return x >= 0 && z >= 0 && x <= this.size && z <= this.size; + } }; // Check bounds on tile class Map.prototype.validClass = function(c) { return c >= 0 && c < this.tileClasses.length; }; Map.prototype.getTexture = function(x, z) { if (!this.validT(x, z)) { throw("getTexture: invalid tile position ("+x+", "+z+")"); } return this.IDToName[this.texture[x][z]]; }; Map.prototype.setTexture = function(x, z, texture) { if (!this.validT(x, z)) { throw("setTexture: invalid tile position ("+x+", "+z+")"); } this.texture[x][z] = this.getTextureID(texture); }; Map.prototype.getHeight = function(x, z) { if (!this.validH(x, z)) { throw("getHeight: invalid vertex position ("+x+", "+z+")"); } return this.height[x][z]; }; Map.prototype.setHeight = function(x, z, height) { if (!this.validH(x, z)) { throw("setHeight: invalid vertex position ("+x+", "+z+")"); } this.height[x][z] = height; }; Map.prototype.getTerrainObjects = function(x, z) { if (!this.validT(x, z)) { throw("getTerrainObjects: invalid tile position ("+x+", "+z+")"); } return this.terrainObjects[x][z]; }; Map.prototype.setTerrainObject = function(x, z, object) { if (!this.validT(x, z)) { throw("setTerrainObject: invalid tile position ("+x+", "+z+")"); } this.terrainObjects[x][z] = object; }; Map.prototype.placeTerrain = function(x, z, terrain) { terrain.place(x, z); }; Map.prototype.addObject = function(obj) { this.objects.push(obj); }; Map.prototype.createArea = function(placer, painter, constraint) { // Check for multiple painters if (painter instanceof Array) { var painterArray = painter; painter = new MultiPainter(painterArray); } // Check for null constraint if (constraint === undefined || constraint === null) { constraint = new NullConstraint(); } else if (constraint instanceof Array) { // Check for multiple constraints var constraintArray = constraint; constraint = new AndConstraint(constraintArray); } var points = placer.place(constraint); if (!points) return undefined; var newID = ++this.areaID; var area = new Area(points, newID); for (var i=0; i < points.length; i++) { this.area[points[i].x][points[i].z] = newID; } painter.paint(area); return area; }; Map.prototype.createObjectGroup = function(placer, player, constraint) { // Check for null constraint if (constraint === undefined || constraint === null) { constraint = new NullConstraint(); } else if (constraint instanceof Array) { // Check for multiple constraints var constraintArray = constraint; constraint = new AndConstraint(constraintArray); } return placer.place(player, constraint); }; Map.prototype.createTileClass = function() { var newID = this.tileClasses.length; this.tileClasses.push(new TileClass(this.size, newID)); return newID; }; // Get height taking into account terrain curvature Map.prototype.getExactHeight = function(x, z) { var xi = min(Math.floor(x), this.size); var zi = min(Math.floor(z), this.size); var xf = x - xi; var zf = z - zi; var h00 = this.height[xi][zi]; var h01 = this.height[xi][zi+1]; var h10 = this.height[xi+1][zi]; var h11 = this.height[xi+1][zi+1]; return ( 1 - zf ) * ( ( 1 - xf ) * h00 + xf * h10 ) + zf * ( ( 1 - xf ) * h01 + xf * h11 ) ; }; +// Converts from the tile centered height map to the corner based height map, used when TILE_CENTERED_HEIGHT_MAP = true +Map.prototype.cornerHeight = function(x, z) +{ + var count = 0; + var sumHeight = 0; + + var dirs = [[-1,-1], [-1,0], [0,-1], [0,0]]; + for each (var dir in dirs) + { + if (this.validH(x + dir[0], z + dir[1])) + { + count++; + sumHeight += this.height[x + dir[0]][z + dir[1]]; + } + } + + if (count == 0) + return 0; + + return sumHeight / count; +}; + Map.prototype.getMapData = function() { var data = {}; // Build entity array var entities = []; // Terrain objects first (trees) var size = this.size; for (var x = 0; x < size; ++x) { for (var z = 0; z < size; ++z) { if (this.terrainObjects[x][z] !== undefined) { entities.push(this.terrainObjects[x][z]); } } } // Now other entities for (var i = 0; i < this.objects.length; ++i) { entities.push(this.objects[i]); } data["entities"] = entities; log("Number of entities: "+entities.length); // Terrain data["size"] = this.size; // Convert 2D heightmap array to flat array // Flat because it's easier to handle by the engine var mapSize = size+1; var height16 = new Uint16Array(mapSize*mapSize); // uint16 for (var x = 0; x < mapSize; x++) { for (var z = 0; z < mapSize; z++) { - var intHeight = Math.floor((this.height[x][z] + SEA_LEVEL) * HEIGHT_UNITS_PER_METRE); + var intHeight; + if (TILE_CENTERED_HEIGHT_MAP) + { + intHeight = Math.floor((this.cornerHeight(x, z) + SEA_LEVEL) * HEIGHT_UNITS_PER_METRE); + } + else + { + intHeight = Math.floor((this.height[x][z] + SEA_LEVEL) * HEIGHT_UNITS_PER_METRE); + } // Prevent under/overflow in terrain data if (intHeight > 0xFFFF) { intHeight = 0xFFFF; } else if (intHeight < 0) { intHeight = 0; } height16[z*mapSize + x] = intHeight; } } data["height"] = height16; data["seaLevel"] = SEA_LEVEL; // Get array of textures used in this map var textureNames = []; for (var name in this.nameToID) { textureNames.push(name); } data["textureNames"] = textureNames; // Convert 2D tile data to flat array var tileIndex = new Uint16Array(size*size); // uint16 var tilePriority = new Uint16Array(size*size); // uint16 for (var x = 0; x < size; x++) { for (var z = 0; z < size; z++) { // TODO: For now just use the texture's index as priority, might want to do this another way tileIndex[z*size + x] = this.texture[x][z]; tilePriority[z*size + x] = this.texture[x][z]; } } data["tileData"] = {"index": tileIndex, "priority": tilePriority}; return data; };