<commit_before>define([
		'goo/addons/terrainpack/Terrain',
		'goo/addons/terrainpack/Vegetation',
		'goo/addons/terrainpack/Forrest',
		'goo/math/Vector3',
		'goo/util/Ajax',
		'goo/math/Transform',
		'goo/math/MathUtils',
		'goo/renderer/Texture',
		'goo/renderer/TextureCreator',
		'goo/util/rsvp'
	],
	function(
		Terrain,
		Vegetation,
		Forrest,
		Vector3,
		Ajax,
		Transform,
		MathUtils,
		Texture,
		TextureCreator,
		RSVP
	) {
		'use strict';

		function TerrainHandler(goo, terrainSize, clipmapLevels, resourceFolder) {
			this.goo = goo;
			this.terrainSize = terrainSize;
			this.resourceFolder = resourceFolder;
			this.terrain = new Terrain(goo, this.terrainSize, clipmapLevels);
			this.vegetation = new Vegetation();
			this.forrest = new Forrest();

			this.hidden = false;
			this.store = new Vector3();
			this.settings = null;
			this.pick = true;
			this.draw = false;
			this.eventX = 0;
			this.eventY = 0;
		}

		TerrainHandler.prototype.isEditing = function () {
			return !this.hidden;
		};

		TerrainHandler.prototype.getHeightAt = function (pos) {
			return this.terrainQuery ? this.terrainQuery.getHeightAt(pos) : 0;
		};

		var LMB = false;
		var altKey = false;

		var mousedown = function (e) {
			if (e.button === 0) {
				this.eventX = e.clientX;
				this.eventY = e.clientY;

				LMB = true;
				altKey = e.altKey;

				this.pick = true;
				this.draw = true;
				console.log('mousedown');
			}
		};

		var mouseup = function (e) {
			if (e.button === 0) {
				LMB = false;
				this.draw = false;
				console.log('mouseup');
			}
		};

		var mousemove = function (e) {
			this.eventX = e.clientX;
			this.eventY = e.clientY;

			this.pick = true;

			if (LMB) {
				altKey = e.altKey;
				this.draw = true;
			}
		};

		TerrainHandler.prototype.toggleEditMode = function () {
			this.terrain.toggleMarker();

			this.hidden = !this.hidden;

			if (this.hidden) {
				this.goo.renderer.domElement.addEventListener("mousedown", mousedown.bind(this), false);
				this.goo.renderer.domElement.addEventListener("mouseup", mouseup.bind(this), false);
				this.goo.renderer.domElement.addEventListener("mouseout", mouseup.bind(this), false);
				this.goo.renderer.domElement.addEventListener("mousemove", mousemove.bind(this), false);
			} else {
				this.goo.renderer.domElement.removeEventListener("mousedown", mousedown);
				this.goo.renderer.domElement.removeEventListener("mouseup", mouseup);
				this.goo.renderer.domElement.removeEventListener("mouseout", mouseup);
				this.goo.renderer.domElement.removeEventListener("mousemove", mousemove);
				this.terrainInfo = this.terrain.getTerrainData();
				this.draw = false;
				LMB = false;
			}

			this.forrest.toggle();
			this.vegetation.toggle();
		};

		TerrainHandler.prototype.initLevel = function (terrainData, settings, forrestLODEntityMap) {
			this.settings = settings;
			var terrainSize = this.terrainSize;

			var terrainPromise = this._loadData(terrainData.heightMap);
			var splatPromise = this._loadData(terrainData.splatMap);

			return RSVP.all([terrainPromise, splatPromise]).then(function (datas) {
				var terrainBuffer = datas[0];
				var splatBuffer = datas[1];

				var terrainArray;
				if (terrainBuffer) {
					terrainArray = new Float32Array(terrainBuffer);
				} else {
					terrainArray = new Float32Array(terrainSize * terrainSize);
				}

				var splatArray;
				if (splatBuffer) {
					splatArray = new Uint8Array(splatBuffer);
				} else {
					splatArray = new Uint8Array(terrainSize * terrainSize * 4 * 4);
				}

				return this._load(terrainData, terrainArray, splatArray, forrestLODEntityMap);
			}.bind(this));
		};

		TerrainHandler.prototype._loadData = function (url) {
			var promise = new RSVP.Promise();

			var ajax = new Ajax();
			ajax.get({
				url: this.resourceFolder + url,
				responseType: 'arraybuffer'
			}).then(function (request) {
				promise.resolve(request.response);
			}.bind(this), function () {
				promise.resolve(null);
			}.bind(this));

			return promise;
		};

		TerrainHandler.prototype._textureLoad = function (url) {
			var promise = new RSVP.Promise();
			new TextureCreator().loadTexture2D(url, {
				anisotropy: 4
			}, function (texture) {
				promise.resolve(texture);
			});
			return promise;
		};

		TerrainHandler.prototype._load = function (terrainData, parentMipmap, splatMap, forrestLODEntityMap) {
			var promises = [];
			promises.push(this._textureLoad(this.resourceFolder + terrainData.ground1.texture));
			promises.push(this._textureLoad(this.resourceFolder + terrainData.ground2.texture));
			promises.push(this._textureLoad(this.resourceFolder + terrainData.ground3.texture));
			promises.push(this._textureLoad(this.resourceFolder + terrainData.ground4.texture));
			promises.push(this._textureLoad(this.resourceFolder + terrainData.ground5.texture));
			promises.push(this._textureLoad(this.resourceFolder + terrainData.stone.texture));
			return RSVP.all(promises).then(function (textures) {
				this.terrain.init({
					heightMap: parentMipmap,
					splatMap: splatMap,
					ground1: textures[0],
					ground2: textures[1],
					ground3: textures[2],
					ground4: textures[3],
					ground5: textures[4],
					stone: textures[5]
				});
				this.terrainInfo = this.terrain.getTerrainData();

				var terrainSize = this.terrainSize;
				var calcVec = new Vector3();
				var terrainQuery = this.terrainQuery = {
					getHeightAt: function (pos) {
						if (pos[0] < 0 || pos[0] > terrainSize - 1 || pos[2] < 0 || pos[2] > terrainSize - 1) {
							return -1000;
						}

						var x = pos[0];
						var z = terrainSize - pos[2];

						var col = Math.floor(x);
						var row = Math.floor(z);

						var intOnX = x - col,
							intOnZ = z - row;

						var col1 = col + 1;
						var row1 = row + 1;

						col = MathUtils.moduloPositive(col, terrainSize);
						row = MathUtils.moduloPositive(row, terrainSize);
						col1 = MathUtils.moduloPositive(col1, terrainSize);
						row1 = MathUtils.moduloPositive(row1, terrainSize);

						var topLeft = this.terrainInfo.heights[row * terrainSize + col];
						var topRight = this.terrainInfo.heights[row * terrainSize + col1];
						var bottomLeft = this.terrainInfo.heights[row1 * terrainSize + col];
						var bottomRight = this.terrainInfo.heights[row1 * terrainSize + col1];

						return MathUtils.lerp(intOnZ, MathUtils.lerp(intOnX, topLeft, topRight),
							MathUtils.lerp(intOnX, bottomLeft, bottomRight));
					}.bind(this),
					getNormalAt: function (pos) {
						var x = pos[0];
						var z = terrainSize - pos[2];

						var col = Math.floor(x);
						var row = Math.floor(z);

						var col1 = col + 1;
						var row1 = row + 1;

						col = MathUtils.moduloPositive(col, terrainSize);
						row = MathUtils.moduloPositive(row, terrainSize);
						col1 = MathUtils.moduloPositive(col1, terrainSize);
						row1 = MathUtils.moduloPositive(row1, terrainSize);

						var topLeft = this.terrainInfo.heights[row * terrainSize + col];
						var topRight = this.terrainInfo.heights[row * terrainSize + col1];
						var bottomLeft = this.terrainInfo.heights[row1 * terrainSize + col];

						return calcVec.setd((topLeft - topRight), 1, (bottomLeft - topLeft)).normalize();
					}.bind(this),
					getVegetationType: function (xx, zz, slope) {
						var rand = Math.random();
						if (MathUtils.smoothstep(0.82, 0.91, slope) < rand) {
							return null;
						}

						if (this.terrainInfo) {
							xx = Math.floor(xx);
							zz = Math.floor(zz);

							if (xx < 0 || xx > terrainSize - 1 || zz < 0 || zz > terrainSize - 1) {
								return null;
							}

							xx *= this.terrain.splatMult;
							zz *= this.terrain.splatMult;

							var index = (zz * terrainSize * this.terrain.splatMult + xx) * 4;
							var splat1 = this.terrainInfo.splat[index + 0] / 255.0;
							var splat2 = this.terrainInfo.splat[index + 1] / 255.0;
							var splat3 = this.terrainInfo.splat[index + 2] / 255.0;
							var splat4 = this.terrainInfo.splat[index + 3] / 255.0;
							var type = splat1 > rand ? terrainData.ground2 : splat2 > rand ? terrainData.ground3 : splat3 > rand ? terrainData.ground4 : splat4 > rand ? terrainData.ground5 : terrainData.ground1;

							var test = 0;
							for (var veg in type.vegetation) {
								test += type.vegetation[veg];
								if (rand < test) {
									return veg;
								}
							}
							return null;
						}
						return null;
					}.bind(this),
					getForrestType: function (xx, zz, slope, rand) {
						if (MathUtils.smoothstep(0.8, 0.88, slope) < rand) {
							return null;
						}

						if (this.terrainInfo) {
							xx = Math.floor(xx);
							zz = Math.floor(zz);

							if (xx < 0 || xx > terrainSize - 1 || zz < 0 || zz > terrainSize - 1) {
								return null;
							}

							xx *= this.terrain.splatMult;
							zz *= this.terrain.splatMult;

							var index = (zz * terrainSize * this.terrain.splatMult + xx) * 4;
							var splat1 = this.terrainInfo.splat[index + 0] / 255.0;
							var splat2 = this.terrainInfo.splat[index + 1] / 255.0;
							var splat3 = this.terrainInfo.splat[index + 2] / 255.0;
							var splat4 = this.terrainInfo.splat[index + 3] / 255.0;
							var type = splat1 > rand ? terrainData.ground2 : splat2 > rand ? terrainData.ground3 : splat3 > rand ? terrainData.ground4 : splat4 > rand ? terrainData.ground5 : terrainData.ground1;

							var test = 0;
							for (var veg in type.forrest) {
								test += type.forrest[veg];
								if (rand < test) {
									return veg;
								}
							}
							return null;
						}
						return null;
					}.bind(this),
                    getType: function (xx, zz, slope, rand) {
                        if (MathUtils.smoothstep(0.8, 0.88, slope) < rand) {
                            return terrainData.stone;
                        }

                        if (this.terrainInfo) {
                            xx = Math.floor(xx);
                            zz = Math.floor(zz);

                            if (xx < 0 || xx > terrainSize - 1 || zz < 0 || zz > terrainSize - 1) {
                                return terrainData.stone;
                            }

                            xx *= this.terrain.splatMult;
                            zz *= this.terrain.splatMult;

                            var index = (zz * terrainSize * this.terrain.splatMult + xx) * 4;
                            var splat1 = this.terrainInfo.splat[index + 0] / 255.0;
                            var splat2 = this.terrainInfo.splat[index + 1] / 255.0;
                            var splat3 = this.terrainInfo.splat[index + 2] / 255.0;
                            var splat4 = this.terrainInfo.splat[index + 3] / 255.0;
                            var type = splat1 > rand ? terrainData.ground2 : splat2 > rand ? terrainData.ground3 : splat3 > rand ? terrainData.ground4 : splat4 > rand ? terrainData.ground5 : terrainData.ground1;

                            return type;
                        }
                        return terrainData.stone;
                    }.bind(this)
				};

				var vegetationAtlasTexture = new TextureCreator().loadTexture2D(this.resourceFolder + terrainData.vegetationAtlas);
				vegetationAtlasTexture.anisotropy = 4;
				var vegetationTypes = terrainData.vegetationTypes;

				var forrestAtlasTexture = new TextureCreator().loadTexture2D(this.resourceFolder + terrainData.forrestAtlas);
				forrestAtlasTexture.anisotropy = 4;
				var forrestAtlasNormals = new TextureCreator().loadTexture2D(this.resourceFolder + terrainData.forrestAtlasNormals);
				var forrestTypes = terrainData.forrestTypes;

				this.vegetation.init(this.goo.world, terrainQuery, vegetationAtlasTexture, vegetationTypes);
				this.forrest.init(this.goo.world, terrainQuery, forrestAtlasTexture, forrestAtlasNormals, forrestTypes, forrestLODEntityMap);
			}.bind(this));
		};

		TerrainHandler.prototype.updatePhysics = function () {
			this.terrain.updateAmmoBody();
		};

		TerrainHandler.prototype.initPhysics = function () {
			this.ammoBody = this.terrain.initAmmoBody();
		};

		TerrainHandler.prototype.update = function (cameraEntity) {<commit_msg>- Added promise for the texture loading in TerrainHandler, so that init   does not complete until all textures are fully loaded.<commit_after>define([
		'goo/addons/terrainpack/Terrain',
		'goo/addons/terrainpack/Vegetation',
		'goo/addons/terrainpack/Forrest',
		'goo/math/Vector3',
		'goo/util/Ajax',
		'goo/math/Transform',
		'goo/math/MathUtils',
		'goo/renderer/Texture',
		'goo/renderer/TextureCreator',
		'goo/util/rsvp'
	],
	function(
		Terrain,
		Vegetation,
		Forrest,
		Vector3,
		Ajax,
		Transform,
		MathUtils,
		Texture,
		TextureCreator,
		RSVP
	) {
		'use strict';

		function TerrainHandler(goo, terrainSize, clipmapLevels, resourceFolder) {
			this.goo = goo;
			this.terrainSize = terrainSize;
			this.resourceFolder = resourceFolder;
			this.terrain = new Terrain(goo, this.terrainSize, clipmapLevels);
			this.vegetation = new Vegetation();
			this.forrest = new Forrest();

			this.hidden = false;
			this.store = new Vector3();
			this.settings = null;
			this.pick = true;
			this.draw = false;
			this.eventX = 0;
			this.eventY = 0;
		}

		TerrainHandler.prototype.isEditing = function () {
			return !this.hidden;
		};

		TerrainHandler.prototype.getHeightAt = function (pos) {
			return this.terrainQuery ? this.terrainQuery.getHeightAt(pos) : 0;
		};

		var LMB = false;
		var altKey = false;

		var mousedown = function (e) {
			if (e.button === 0) {
				this.eventX = e.clientX;
				this.eventY = e.clientY;

				LMB = true;
				altKey = e.altKey;

				this.pick = true;
				this.draw = true;
				console.log('mousedown');
			}
		};

		var mouseup = function (e) {
			if (e.button === 0) {
				LMB = false;
				this.draw = false;
				console.log('mouseup');
			}
		};

		var mousemove = function (e) {
			this.eventX = e.clientX;
			this.eventY = e.clientY;

			this.pick = true;

			if (LMB) {
				altKey = e.altKey;
				this.draw = true;
			}
		};

		TerrainHandler.prototype.toggleEditMode = function () {
			this.terrain.toggleMarker();

			this.hidden = !this.hidden;

			if (this.hidden) {
				this.goo.renderer.domElement.addEventListener("mousedown", mousedown.bind(this), false);
				this.goo.renderer.domElement.addEventListener("mouseup", mouseup.bind(this), false);
				this.goo.renderer.domElement.addEventListener("mouseout", mouseup.bind(this), false);
				this.goo.renderer.domElement.addEventListener("mousemove", mousemove.bind(this), false);
			} else {
				this.goo.renderer.domElement.removeEventListener("mousedown", mousedown);
				this.goo.renderer.domElement.removeEventListener("mouseup", mouseup);
				this.goo.renderer.domElement.removeEventListener("mouseout", mouseup);
				this.goo.renderer.domElement.removeEventListener("mousemove", mousemove);
				this.terrainInfo = this.terrain.getTerrainData();
				this.draw = false;
				LMB = false;
			}

			this.forrest.toggle();
			this.vegetation.toggle();
		};

		TerrainHandler.prototype.initLevel = function (terrainData, settings, forrestLODEntityMap) {
			this.settings = settings;
			var terrainSize = this.terrainSize;

			var terrainPromise = this._loadData(terrainData.heightMap);
			var splatPromise = this._loadData(terrainData.splatMap);

			return RSVP.all([terrainPromise, splatPromise]).then(function (datas) {
				var terrainBuffer = datas[0];
				var splatBuffer = datas[1];

				var terrainArray;
				if (terrainBuffer) {
					terrainArray = new Float32Array(terrainBuffer);
				} else {
					terrainArray = new Float32Array(terrainSize * terrainSize);
				}

				var splatArray;
				if (splatBuffer) {
					splatArray = new Uint8Array(splatBuffer);
				} else {
					splatArray = new Uint8Array(terrainSize * terrainSize * 4 * 4);
				}

				return this._load(terrainData, terrainArray, splatArray, forrestLODEntityMap);
			}.bind(this));
		};

		TerrainHandler.prototype._loadData = function (url) {
			var promise = new RSVP.Promise();

			var ajax = new Ajax();
			ajax.get({
				url: this.resourceFolder + url,
				responseType: 'arraybuffer'
			}).then(function (request) {
				promise.resolve(request.response);
			}.bind(this), function () {
				promise.resolve(null);
			}.bind(this));

			return promise;
		};

		TerrainHandler.prototype._textureLoad = function (url) {
			var promise = new RSVP.Promise();
			new TextureCreator().loadTexture2D(url, {
				anisotropy: 4
			}, function (texture) {
				promise.resolve(texture);
			});
			return promise;
		};

		TerrainHandler.prototype._load = function (terrainData, parentMipmap, splatMap, forrestLODEntityMap) {
			var promises = [];
			promises.push(this._textureLoad(this.resourceFolder + terrainData.ground1.texture));
			promises.push(this._textureLoad(this.resourceFolder + terrainData.ground2.texture));
			promises.push(this._textureLoad(this.resourceFolder + terrainData.ground3.texture));
			promises.push(this._textureLoad(this.resourceFolder + terrainData.ground4.texture));
			promises.push(this._textureLoad(this.resourceFolder + terrainData.ground5.texture));
			promises.push(this._textureLoad(this.resourceFolder + terrainData.stone.texture));
			return RSVP.all(promises).then(function (textures) {
				this.terrain.init({
					heightMap: parentMipmap,
					splatMap: splatMap,
					ground1: textures[0],
					ground2: textures[1],
					ground3: textures[2],
					ground4: textures[3],
					ground5: textures[4],
					stone: textures[5]
				});
				this.terrainInfo = this.terrain.getTerrainData();

				var terrainSize = this.terrainSize;
				var calcVec = new Vector3();
				var terrainQuery = this.terrainQuery = {
					getHeightAt: function (pos) {
						if (pos[0] < 0 || pos[0] > terrainSize - 1 || pos[2] < 0 || pos[2] > terrainSize - 1) {
							return -1000;
						}

						var x = pos[0];
						var z = terrainSize - pos[2];

						var col = Math.floor(x);
						var row = Math.floor(z);

						var intOnX = x - col,
							intOnZ = z - row;

						var col1 = col + 1;
						var row1 = row + 1;

						col = MathUtils.moduloPositive(col, terrainSize);
						row = MathUtils.moduloPositive(row, terrainSize);
						col1 = MathUtils.moduloPositive(col1, terrainSize);
						row1 = MathUtils.moduloPositive(row1, terrainSize);

						var topLeft = this.terrainInfo.heights[row * terrainSize + col];
						var topRight = this.terrainInfo.heights[row * terrainSize + col1];
						var bottomLeft = this.terrainInfo.heights[row1 * terrainSize + col];
						var bottomRight = this.terrainInfo.heights[row1 * terrainSize + col1];

						return MathUtils.lerp(intOnZ, MathUtils.lerp(intOnX, topLeft, topRight),
							MathUtils.lerp(intOnX, bottomLeft, bottomRight));
					}.bind(this),
					getNormalAt: function (pos) {
						var x = pos[0];
						var z = terrainSize - pos[2];

						var col = Math.floor(x);
						var row = Math.floor(z);

						var col1 = col + 1;
						var row1 = row + 1;

						col = MathUtils.moduloPositive(col, terrainSize);
						row = MathUtils.moduloPositive(row, terrainSize);
						col1 = MathUtils.moduloPositive(col1, terrainSize);
						row1 = MathUtils.moduloPositive(row1, terrainSize);

						var topLeft = this.terrainInfo.heights[row * terrainSize + col];
						var topRight = this.terrainInfo.heights[row * terrainSize + col1];
						var bottomLeft = this.terrainInfo.heights[row1 * terrainSize + col];

						return calcVec.setd((topLeft - topRight), 1, (bottomLeft - topLeft)).normalize();
					}.bind(this),
					getVegetationType: function (xx, zz, slope) {
						var rand = Math.random();
						if (MathUtils.smoothstep(0.82, 0.91, slope) < rand) {
							return null;
						}

						if (this.terrainInfo) {
							xx = Math.floor(xx);
							zz = Math.floor(zz);

							if (xx < 0 || xx > terrainSize - 1 || zz < 0 || zz > terrainSize - 1) {
								return null;
							}

							xx *= this.terrain.splatMult;
							zz *= this.terrain.splatMult;

							var index = (zz * terrainSize * this.terrain.splatMult + xx) * 4;
							var splat1 = this.terrainInfo.splat[index + 0] / 255.0;
							var splat2 = this.terrainInfo.splat[index + 1] / 255.0;
							var splat3 = this.terrainInfo.splat[index + 2] / 255.0;
							var splat4 = this.terrainInfo.splat[index + 3] / 255.0;
							var type = splat1 > rand ? terrainData.ground2 : splat2 > rand ? terrainData.ground3 : splat3 > rand ? terrainData.ground4 : splat4 > rand ? terrainData.ground5 : terrainData.ground1;

							var test = 0;
							for (var veg in type.vegetation) {
								test += type.vegetation[veg];
								if (rand < test) {
									return veg;
								}
							}
							return null;
						}
						return null;
					}.bind(this),
					getForrestType: function (xx, zz, slope, rand) {
						if (MathUtils.smoothstep(0.8, 0.88, slope) < rand) {
							return null;
						}

						if (this.terrainInfo) {
							xx = Math.floor(xx);
							zz = Math.floor(zz);

							if (xx < 0 || xx > terrainSize - 1 || zz < 0 || zz > terrainSize - 1) {
								return null;
							}

							xx *= this.terrain.splatMult;
							zz *= this.terrain.splatMult;

							var index = (zz * terrainSize * this.terrain.splatMult + xx) * 4;
							var splat1 = this.terrainInfo.splat[index + 0] / 255.0;
							var splat2 = this.terrainInfo.splat[index + 1] / 255.0;
							var splat3 = this.terrainInfo.splat[index + 2] / 255.0;
							var splat4 = this.terrainInfo.splat[index + 3] / 255.0;
							var type = splat1 > rand ? terrainData.ground2 : splat2 > rand ? terrainData.ground3 : splat3 > rand ? terrainData.ground4 : splat4 > rand ? terrainData.ground5 : terrainData.ground1;

							var test = 0;
							for (var veg in type.forrest) {
								test += type.forrest[veg];
								if (rand < test) {
									return veg;
								}
							}
							return null;
						}
						return null;
					}.bind(this),
                    getType: function (xx, zz, slope, rand) {
                        if (MathUtils.smoothstep(0.8, 0.88, slope) < rand) {
                            return terrainData.stone;
                        }

                        if (this.terrainInfo) {
                            xx = Math.floor(xx);
                            zz = Math.floor(zz);

                            if (xx < 0 || xx > terrainSize - 1 || zz < 0 || zz > terrainSize - 1) {
                                return terrainData.stone;
                            }

                            xx *= this.terrain.splatMult;
                            zz *= this.terrain.splatMult;

                            var index = (zz * terrainSize * this.terrain.splatMult + xx) * 4;
                            var splat1 = this.terrainInfo.splat[index + 0] / 255.0;
                            var splat2 = this.terrainInfo.splat[index + 1] / 255.0;
                            var splat3 = this.terrainInfo.splat[index + 2] / 255.0;
                            var splat4 = this.terrainInfo.splat[index + 3] / 255.0;
                            var type = splat1 > rand ? terrainData.ground2 : splat2 > rand ? terrainData.ground3 : splat3 > rand ? terrainData.ground4 : splat4 > rand ? terrainData.ground5 : terrainData.ground1;

                            return type;
                        }
                        return terrainData.stone;
                    }.bind(this)
				};

				var texturesPromise = new RSVP.Promise();
				var loadCount = 3;
				var onLoaded = function() {
					if (--loadCount)
						texturesPromise.resolve();
				};

				var vegetationAtlasTexture = new TextureCreator().loadTexture2D(this.resourceFolder + terrainData.vegetationAtlas, {}, onLoaded);

				vegetationAtlasTexture.anisotropy = 4;
				var vegetationTypes = terrainData.vegetationTypes;

				var forrestAtlasTexture = new TextureCreator().loadTexture2D(this.resourceFolder + terrainData.forrestAtlas, {}, onLoaded);

				forrestAtlasTexture.anisotropy = 4;
				var forrestAtlasNormals = new TextureCreator().loadTexture2D(this.resourceFolder + terrainData.forrestAtlasNormals, {}, onLoaded);

				var forrestTypes = terrainData.forrestTypes;

				this.vegetation.init(this.goo.world, terrainQuery, vegetationAtlasTexture, vegetationTypes);
				this.forrest.init(this.goo.world, terrainQuery, forrestAtlasTexture, forrestAtlasNormals, forrestTypes, forrestLODEntityMap);

				return texturesPromise;
			}.bind(this));
		};

		TerrainHandler.prototype.updatePhysics = function () {
			this.terrain.updateAmmoBody();
		};

		TerrainHandler.prototype.initPhysics = function () {
			this.ammoBody = this.terrain.initAmmoBody();
		};

		TerrainHandler.prototype.update = function (cameraEntity) {
{'fraction_non_alphanumeric': 0.12410123085672503, 'fraction_numerical': 0.01734573668602998, 'mean_word_length': 2.5745607666618264, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5516875', 'n_tokens_mistral': 10404, 'n_tokens_neox': 7981, 'n_words': 1606}