Terrain tileset painter preview.
Textures from Starsiege:TRIBES. Preview...Not feature-complete atm.
/**
* Copyright Glidias ( http://wonderfl.net/user/Glidias )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/9dsO
*/
package {
import alternativ7.engine3d.controllers.SimpleObjectController;
import alternativ7.engine3d.core.Camera3D;
import alternativ7.engine3d.core.Face;
import alternativ7.engine3d.core.MouseEvent3D;
import alternativ7.engine3d.core.Object3D;
import alternativ7.engine3d.core.Object3DContainer;
import alternativ7.engine3d.core.Vertex;
import alternativ7.engine3d.core.View;
import alternativ7.engine3d.loaders.Parser3DS;
import alternativ7.engine3d.materials.FillMaterial;
import alternativ7.engine3d.objects.Mesh;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
/**
* Terrain tileset painter
*/
public class TerrainPainterPreview extends Sprite {
private var rootContainer:Object3DContainer = new Object3DContainer();
private var camera:Camera3D;
private var controller:SimpleObjectController;
public function TerrainPainterPreview() {
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
// Camera and view
camera = new Camera3D();
camera.view = new View(stage.stageWidth, stage.stageHeight);
addChild(camera.view);
addChild(camera.diagram);
// Initial position
camera.rotationX = -120*Math.PI/180;
camera.y = -400;
camera.z = 600;
controller = new SimpleObjectController(stage, camera, 500);
rootContainer.addChild(camera);
// Listeners
stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
stage.addEventListener(Event.RESIZE, onResize);
loadModel("http://glidias.freehostia.com/terrain.3ds");
}
private function loadModel(url:String):void
{
var loader3ds:URLLoader = new URLLoader();
loader3ds.dataFormat = URLLoaderDataFormat.BINARY;
loader3ds.load(new URLRequest(url));
loader3ds.addEventListener(Event.COMPLETE, on3dsLoad);
}
private function setupOrigin():void {
var child:Object3D = rootContainer.addChild( new MarkerBox(0xFF0000, 100) );
child.x = 100;
child = rootContainer.addChild( new MarkerBox(0x00FF00, 10, 100) );
child.y = 100;
child = rootContainer.addChild( new MarkerBox(0x0000FF, 10, 10, 100) );
child.z = 100;
}
private function on3dsLoad(e:Event):void
{
e.currentTarget.removeEventListener(e.type, on3dsLoad);
parseTerrain ( (e.target as URLLoader).data );
prepareTileSet();
setupOrigin();
}
private var testMaterial:FillMaterial = new FillMaterial( 0xFFDDCC);
private var terrainGridPainter:TerrainGridPainter;
private var tileSetViewer:TileSetViewer;
private function parseTerrain(data:*):void {
var parser:Parser3DS = new Parser3DS();
parser.parse(data);
var mesh:Mesh = (parser.objects[0] as Mesh);
mesh.setMaterialToAllFaces( testMaterial);
mesh.sorting = 1;
terrainGridPainter = new TerrainGridPainter();
terrainGridPainter.buildFromMesh(mesh);
terrainGridPainter.addEventListener(MouseEvent3D.CLICK, paintOnTerrain);
rootContainer.addChild(terrainGridPainter);
}
private function paintOnTerrain(e:MouseEvent3D):void
{
if (tileSetViewer.model == null) return;
terrainGridPainter.paintUsingRay(e.localOrigin, e.localDirection, tileSetViewer.model);
}
private function prepareTileSet():void {
tileSetViewer = new TileSetViewer();
//tileSetViewer.directoryPath = "http://localhost/tribes/textures/lushdml";
tileSetViewer.directoryPath = "http://glidias.freehostia.com/tribes/textures/lushdml";
tileSetViewer.loadForXMLFromURL(tileSetViewer.directoryPath + "/index.php");
tileSetViewer.addEventListener(TileSetViewer.APPLY_ALL, onTileSetApplyAll, false, 0, true);
addChild(tileSetViewer);
}
private function onTileSetApplyAll(e:Event):void
{
terrainGridPainter.applyAll(tileSetViewer.model);
}
private function onEnterFrame(e:Event):void {
controller.update();
camera.render();
}
private function onResize(e:Event = null):void {
// Width and height of view
camera.view.width = stage.stageWidth;
camera.view.height = stage.stageHeight;
}
}
}
//package utils.terrain
//{
/**
* ...
* @author Glenn Ko
*/
//public
class PaintCommand
{
public var textureDef:TextureDefinition;
public var patch:Patch;
public function PaintCommand(patch:Patch, textureDef:TextureDefinition) {
this.patch = patch;
this.textureDef = textureDef;
}
public function execute():void {
patch.textureDef = textureDef;
}
}
//}
//package utils.terrain
//{
import alternativ7.engine3d.materials.TextureMaterial;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.geom.Matrix;
/**
* A quad-based texture definition.
* @author Glenn Ko
*/
//public
class TextureDefinition
{
public var material:TextureMaterial;
public var stringId:String;
public var identityTypeLayout:Vector.<int> = new Vector.<int>(4, true);
public var oddType:int;
private static var HW:int = 64;
// precompute all possible transforms
private static const TRANSFORMS:Vector.<Matrix> = createStaticTransforms();
private static const TRANSFORM_LAYOUTS:Vector.<Vector.<int>> = createStaticTransformLayouts();
private static function createStaticTransforms():Vector.<Matrix> {
var matrixList:Vector.<Matrix> = new Vector.<Matrix>(7, true);
var mat:Matrix;
// rotate 90 degrees clockwise successively
matrixList[0] = mat = new Matrix(); mat.translate(-HW, -HW); mat.rotate( .5*Math.PI); mat.translate(HW, HW);
matrixList[1] = mat = new Matrix(); mat.translate( -HW, -HW); mat.rotate( 1 * Math.PI); mat.translate(HW, HW);
matrixList[2] = mat = new Matrix(); mat.translate( -HW, -HW); mat.rotate( 1.5 * Math.PI); mat.translate(HW, HW);
// flip vertical and rotate 90 degrees anticlockwise successively
matrixList[3] = mat = new Matrix(); mat.translate(-HW, -HW); mat.scale(1, -1); mat.translate(HW, HW);
matrixList[4] = mat = new Matrix(); mat.translate(-HW, -HW); mat.scale(1, -1); mat.rotate( -.5 * Math.PI); mat.translate(HW, HW);
matrixList[5] = mat = new Matrix(); mat.translate(-HW, -HW); mat.scale(1, -1); mat.rotate( -1 * Math.PI); mat.translate(HW, HW);
matrixList[6] = mat = new Matrix(); mat.translate(-HW, -HW); mat.scale(1, -1); mat.rotate( -1.5 * Math.PI); mat.translate(HW, HW);
return matrixList;
}
private static function createStaticTransformLayouts():Vector.<Vector.<int>> {
var vec:Vector.<Vector.<int>> = new Vector.<Vector.<int>>(7, true);
var layout:Vector.<int>;
vec[0] = layout = new <int>[2,0,3,1]; layout.fixed = true;
vec[1] = layout = new <int>[3,2,1,0]; layout.fixed = true;
vec[2] = layout = new <int>[1,3,0,2]; layout.fixed = true;
vec[3] = layout = new <int>[2,3,0,1]; layout.fixed = true;
vec[4] = layout = new <int>[3,1,2,0]; layout.fixed = true;
vec[5] = layout = new <int>[1,0,3,2]; layout.fixed = true;
vec[6] = layout = new <int>[0,2,1,3]; layout.fixed = true;
return vec;
}
public static function previewTransforms(texture:BitmapData):Sprite {
var bmpData:BitmapData;
var spr:Sprite = new Sprite();
for (var i:int = 0; i < 7; i++) {
bmpData = new BitmapData(texture.width, texture.height, texture.transparent, 0);
bmpData.draw(texture, TRANSFORMS[i]);
var child:DisplayObject = spr.addChild( new Bitmap(bmpData) );
child.x = i * (bmpData.width + 2);
}
return spr;
}
private var _transformCache:Vector.<TextureDefinition>;
public function TextureDefinition()
{
}
public function toString():String {
return "[TextureDefinition "+identityTypeLayout+"]"
}
/**
* The odd type is used to quickly determine within a common quad hash combination
* (ie. their type combinations match) whether the quantities also match for each given type.
* */
public function calculateOddType():void {
oddType = getOddType(identityTypeLayout);
}
public function createChildTransform(request:Vector.<int>):TextureDefinition {
// find index based on request
for (var i:int = 0; i < 7; i++) {
var layout:Vector.<int> = TRANSFORM_LAYOUTS[i];
if ( request[0] == identityTypeLayout[layout[0]] && request[1] == identityTypeLayout[layout[1]] && request[2] == identityTypeLayout[layout[2]] && request[3] == identityTypeLayout[layout[3]] ) {
break;
}
}
if ( i < 7) {
if (_transformCache == null) _transformCache = new Vector.<TextureDefinition>(7, true)
else if (_transformCache[i] != null) return _transformCache[i];
//try {
var texDef:TextureDefinition = new TextureDefinition();
var bmpData:BitmapData = material.texture;
bmpData = new BitmapData(bmpData.width, bmpData.height, bmpData.transparent, 0);
bmpData.draw(material.texture, TRANSFORMS[i]);
var textureMaterial:TextureMaterial = new TextureMaterial(bmpData, material.repeat, material.smooth, material.mipMapping, material.resolution);
texDef.material = textureMaterial;
texDef.identityTypeLayout[0] = identityTypeLayout[layout[0]];
texDef.identityTypeLayout[1] = identityTypeLayout[layout[1]];
texDef.identityTypeLayout[2] = identityTypeLayout[layout[2]];
texDef.identityTypeLayout[3] = identityTypeLayout[layout[3]];
texDef.oddType = oddType;
_transformCache[i] = texDef;
return texDef;
//}
// catch (e:Error) {
// throw new Error("CAUGHT!"+material);
// }
}
//throw new Error("Could not resolve given transform!");
return null;
}
public static function getOddType(request:Vector.<int>):int {
var oddType:int = 0;
var a:int = request[0];
var b:int = request[1];
var c:int = request[2];
var d:int = request[3];
var count:int = 0;
if (a == b || a == c || a == d) count++;
if (b == c || b == d) count++;
if (c == d) count++;
count = 4 - count;
// var sb:int = count; // for checking significant bits
if (count == 2) {
// 2 significant bits, either 2/2 (no oddtype) or find odd type (1/3) which is 1 unique type against 3 similar types.
count = 0;
if (a != b) {
oddType = b;
count++;
}
if (a != c) {
oddType = c;
count++;
}
if (a != d) {
oddType = d;
count++;
}
// check for odd type of 1/3
oddType = count != 2 ? count != 1 ? a : oddType // determine odd one out
: 0; // 2-2 case, no odd type
}
else if (count == 3) { // for oddtype, find 2 of the same type among 2 which are different
oddType = a == b || a == c || a == d ? a
: oddType = b == c || b == d ? b : d;
}
// throw new Error("Oddtype:" + oddType + ", " +sb);
return oddType;
}
/**
* Returns collision hash key.
* @return
*/
public function getTypeCombination():int {
return identityTypeLayout[0] | identityTypeLayout[1] | identityTypeLayout[2] | identityTypeLayout[4];
}
}
//package utils.terrain
//{
import alternativ7.engine3d.materials.TextureMaterial;
import flash.utils.Dictionary;
/**
* ...
* @author Glenn Ko
*/
//public
class TileSetModel
{
private var _uniqueTextureCount:int = 0;
private var _idTypeHash:Dictionary = new Dictionary(); // Vector.<TextureDefinition>
private var _charIdMap:Dictionary = new Dictionary();
private var _orderedUniqueTextureDefs:Vector.<TextureDefinition>
private var _orderedUniqueTextures:Vector.<TextureMaterial>;
public function get uniqueTextureCount():int { return _uniqueTextureCount; }
public function get orderedUniqueTextureDefs():Vector.<TextureDefinition> { return _orderedUniqueTextureDefs; }
public function get orderedUniqueTextures():Vector.<TextureMaterial> { return _orderedUniqueTextures; }
public var selectedTexture:TextureDefinition;
public function TileSetModel(vec:Vector.<TextureMaterial>=null)
{
if (vec != null) setNewMaterials(vec);
}
private static const CANDIDATE_STACK:Vector.<TextureDefinition> = new Vector.<TextureDefinition>(4, true);
/**
* Finds a given texture definition given a combination request, or registers/creates
* a new texture definition to match the request if not found yet. This method might
* return null if it can't resolve a given combination.
* @param request The combination request needs a filled integer vector of 4 values for each type,
* reprensenting the 4 sub-quad corner terrain types of a given tile (NW, NE, SW, SE) accordingly.
* @return A matching texture definition, if available.
*/
public function getDefinitionByCombination(request:Vector.<int>):TextureDefinition {
var key:int = request[0] | request[1] | request[2] | request[3];
var candidates:Vector.<TextureDefinition> = _idTypeHash[key];
if (candidates == null) {
return null;
}
var len:int = candidates.length;
var oddType:int = TextureDefinition.getOddType(request);
var count:int = 0;
for (var i:int = 0 ; i < len; i++) {
var def:TextureDefinition = candidates[i];
if (oddType == def.oddType) {
if ( // check for exact match
def.identityTypeLayout[0] == request[0] &&
def.identityTypeLayout[1] == request[1] &&
def.identityTypeLayout[2] == request[2] &&
def.identityTypeLayout[3] == request[3] ) {
//throw new Error(def.stringId);
return def;
}
else { // push to potential candidate list to check for errors
CANDIDATE_STACK[count++] = def;
}
}
if (count > 0) {
if (count > 1) throw new Error("MORe than 1 candidate! Should not be!");
return CANDIDATE_STACK[0].createChildTransform(request);
}
}
return null;
}
public function setNewMaterials(vec:Vector.<TextureMaterial>):void {
_uniqueTextureCount = 0;
var len:int = vec.length;
var mat:TextureMaterial;
var sortArr:Array = [];
var ider:String;
var c:String;
var textureDefStack:Vector.<TextureDefinition> = new Vector.<TextureDefinition>(len, true);
var textureDef:TextureDefinition;
for (var i:int = 0 ; i < len; i++) {
mat = vec[i];
textureDef = new TextureDefinition();
textureDef.material = mat;
textureDefStack[i] = textureDef;
var fileDirArr:Array = mat.diffuseMapURL.split("/");
ider = fileDirArr[fileDirArr.length - 1];
ider = ider.toLowerCase();
ider = ider.slice(0,ider.indexOf("."))
ider = ider.slice(1);
textureDef.stringId = ider;
c = ider.charAt(0);
if ( ( c + c + c + c) === ider) {
_charIdMap[c] = 1 << ++_uniqueTextureCount;
sortArr.push(textureDef);
}
}
sortArr.sortOn("stringId");
_orderedUniqueTextures = new Vector.<TextureMaterial>(sortArr.length, true);
_orderedUniqueTextureDefs = new Vector.<TextureDefinition>(sortArr.length, true);
for (i = 0; i < sortArr.length; i++) {
_orderedUniqueTextureDefs[i] = sortArr[i];
_orderedUniqueTextures[i] = (sortArr[i] as TextureDefinition).material;
}
for (i = 0; i < len; i++) {
textureDef = textureDefStack[i];
var identityLayout:Vector.<int> = textureDef.identityTypeLayout;
ider = textureDef.stringId;
var key:int =
(identityLayout[0] = _charIdMap[ider.charAt(0)]) |
(identityLayout[1] = _charIdMap[ider.charAt(1)]) |
(identityLayout[2] = _charIdMap[ider.charAt(2)]) |
(identityLayout[3] = _charIdMap[ider.charAt(3)]);
textureDef.calculateOddType();
var textureDefList:Vector.<TextureDefinition> = _idTypeHash[key] || (_idTypeHash[key] = new Vector.<TextureDefinition>());
textureDefList.push(textureDef);
}
}
}
//}
//package utils.terrain
//{
import alternativ7.engine3d.loaders.events.LoaderErrorEvent;
import alternativ7.engine3d.loaders.events.LoaderEvent;
import alternativ7.engine3d.loaders.events.LoaderProgressEvent;
import alternativ7.engine3d.loaders.MaterialLoader;
import alternativ7.engine3d.materials.TextureMaterial;
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.text.TextField;
/**
* ...
* @author Glenn Ko
*/
//public
class TileSetViewer extends Sprite
{
private var _materialLoader:MaterialLoader = new MaterialLoader();
public var textureList:Vector.<TextureMaterial>;
private var _partLoaded:int = 0;
public var context:LoaderContext = null;
public var directoryPath:String = "";
public var model:TileSetModel;
public static var APPLY_ALL:String = "tileSetApplyAll";
public function TileSetViewer()
{
}
private function onApplyAllClick(e:MouseEvent):void
{
dispatchEvent( new Event(APPLY_ALL) );
}
public function loadFromDefinitions(classDefs:Array, domain:ApplicationDomain):void {
var len:int = classDefs.length;
textureList = new Vector.<TextureMaterial>(len,true);
for (var i:int = 0; i < len; i++) {
var def:String = classDefs[i];
textureList[i] = new TextureMaterial(new (domain.getDefinition(def) as Class)(0,0), true, true, 1, 2);
addPart(i);
}
model = new TileSetModel(textureList);
}
public function loadForXMLFromURL(url:String):void {
var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, onURLLoadComplete);
urlLoader.load( new URLRequest(url) );
}
private function onURLLoadComplete(e:Event):void
{
// throw new Error("LOAD DONE!");
loadTexturesFromXML(XML(e.target.data));
}
public function loadTexturesFromXML(xml:XML):void {
var xmlList:XMLList = xml.children();
var len:int = xmlList.length();
textureList = new Vector.<TextureMaterial>(len,true);
for (var i:int = 0; i < len; i++) {
var url:String = xmlList[i];
textureList[i] = new TextureMaterial(null, true, true, 2, 2);
textureList[i].diffuseMapURL = directoryPath + "/" + url;
}
_materialLoader.addEventListener(LoaderEvent.PART_COMPLETE, onPartLoadComplete, false, 0, true);
_materialLoader.addEventListener(LoaderProgressEvent.LOADER_PROGRESS, onLoadProgress, false, 0, true);
_materialLoader.addEventListener( LoaderErrorEvent.LOADER_ERROR, onMaterialLoaderError, false, 0, true);
_materialLoader.addEventListener(Event.COMPLETE, onAllLoaded, false, 0, true);
_materialLoader.load(textureList, context);
model = new TileSetModel(textureList);
field = new TextField();
field.x = 64;
addChild(field);
var wrapper:Sprite = _applyAllButton;
var applyAllField:TextField = new TextField();
applyAllField.autoSize = "left";
applyAllField.selectable = false;
applyAllField.text = "Apply All";
_applyAllButton.visible = false;
wrapper.addChild(applyAllField);
wrapper.mouseChildren = false;
wrapper.addEventListener(MouseEvent.CLICK, onApplyAllClick, false , 0, true);
wrapper.buttonMode = true;
wrapper.addChild(applyAllField);
addChild(wrapper);
wrapper.x = 32;
wrapper.y = 16;
}
private var field:TextField;
private function onLoadProgress(e:LoaderProgressEvent):void
{
field.text = e.filesLoaded + "/" +e.filesTotal;
}
private function onMaterialLoaderError(e:LoaderErrorEvent):void
{
}
private function onAllLoaded(e:Event):void
{
loadComplete();
}
private function onPartLoadComplete(e:LoaderEvent):void
{
addPart(e.currentPart);
}
//private var _firstPart:Boolean = true;
private function addPart(currentPart:int):void
{
var textureMat:TextureMaterial = textureList[currentPart];
var index:int;
var textureDefs:Vector.<TextureDefinition> = model.orderedUniqueTextureDefs;
if ( (index=model.orderedUniqueTextures.indexOf(textureMat)) != -1 ) {
var child:DisplayObject = addChild( new Tile(textureMat.texture, textureDefs[index]) );
child.addEventListener(MouseEvent.CLICK, onItemClick, false , 0, true);
child.y = index * (child.height + 4);
}
/*
else {
if (_firstPart) {
addChild( TextureDefinition.previewTransforms(textureMat.texture) );
_firstPart = false;
}
}
*/
}
private var _currentTile:Tile;
private var _applyAllButton:Sprite = new Sprite();
private function onItemClick(e:MouseEvent):void
{
if (_currentTile) _currentTile.selected = false;
_currentTile = (e.currentTarget as Tile);
_currentTile.selected = true;
model.selectedTexture = curTextureSelected;
_applyAllButton.visible = true;
}
private function loadComplete():void {
field.text = "Load done.";
dispatchEvent( new Event(Event.COMPLETE) );
}
public function get curTextureSelected():TextureDefinition { return _currentTile._def; }
}
//}
import flash.display.BitmapData;
import flash.display.Shape;
import flash.display.Sprite;
import flash.geom.Matrix;
//internal
class Tile extends Sprite {
private static const SCALE:Matrix = new Matrix();
public var _def:TextureDefinition;
private var outline:Shape = new Shape();
private var _selected:Boolean;
public function Tile(bmpData:BitmapData, def:TextureDefinition) {
this._def = def;
buttonMode = true;
SCALE.identity();
SCALE.scale(.25, .25);
bmpData = bmpData.clone();
graphics.beginBitmapFill(bmpData, SCALE, false, true);
graphics.drawRect(0, 0, 32,32);
addChild(outline);
outline.graphics.lineStyle(1, 0xFFFFFF, 1);
outline.graphics.drawRect(0, 0, 32, 32);
mouseChildren = false;
}
public function get selected():Boolean { return _selected; }
public function set selected(value:Boolean):void
{
_selected = value;
outline.graphics.clear();
outline.graphics.lineStyle(1, value ? 0xFF0000 : 0xFFFFFF, 1);
outline.graphics.drawRect(0, 0, 32, 32);
mouseEnabled = !value;
}
}
//package utils.terrain
//{
import alternativ7.engine3d.alternativa3d;
import alternativ7.engine3d.core.Camera3D;
import alternativ7.engine3d.core.Canvas;
import alternativ7.engine3d.core.Debug;
import alternativ7.engine3d.core.Face;
import alternativ7.engine3d.core.RayIntersectionData;
import alternativ7.engine3d.objects.Mesh;
import flash.display.BitmapData;
import flash.geom.Vector3D;
import flash.utils.Dictionary;
use namespace alternativa3d;
/**
* A terrain grid mesh for editing and tileset painting.
* @author Glenn Ko
*/
// public
class TerrainGridPainter extends Mesh
{
private var _aabb:Bounds3D = new Bounds3D();
private var _patchMap:Vector.<Vector.<Patch>>;
public var patchList:Vector.<Patch>;
public var pi:int = 0;
public var curPatch:Patch;
public function TerrainGridPainter()
{
}
public function buildFromBitmapData(bmpData:BitmapData, tileWidth:int, tileHeight:int):void {
}
/**
* Generates editable terrain patches from an already existing mesh. The mesh must
* consist purely of ordered triangulated faces per square patch. (ie. triangulated along diagonal
* of quad). It is assumed each patch is of the same grid size.
* @param mesh
*/
public function buildFromMesh(mesh:Mesh):void {
faceList = mesh.faceList;
vertexList = mesh.vertexList;
buildFromFaceList(mesh.faces);
}
public function buildFromFaceList(faceArray:Vector.<Face>):void {
var i:int;
// Generate patch list and entire terrain bounds
var patch:Patch;
var len:int = faceArray.length;
patchList = new Vector.<Patch>(len>>1, true );
for (i = 0 ; i < len; i += 2) {
patch = new Patch( faceArray[i], faceArray[i + 1]);
patchList[i >> 1] = patch;
_aabb.union(patch.aabb);
}
_aabb.writeTo(this);
// Create and fill up patch map from patch list
patch = patchList[0];
var w:Number = boundMaxX - boundMinX;
var h:Number = boundMaxY - boundMinY;
var tw:Number = patch.aabb.getWidth();
var th:Number = patch.aabb.getHeight();
var numXTiles:int = w / tw;
var numYTiles:int = h / th;
_patchMap = new Vector.<Vector.<Patch>>(numYTiles, true);
for (var v:int = 0; v < numYTiles; v++) {
_patchMap[v] = new Vector.<Patch>(numXTiles, true);
}
tw = 1 / tw;
th = 1 / th;
len = patchList.length;
for (i = 0; i < len; i++) {
patch = patchList[i];
patch.xi = (patch.aabb.boundMinX - boundMinX) * tw;
patch.yi = (patch.aabb.boundMinY - boundMinY) * th;
_patchMap[patch.yi][patch.xi] = patch;
}
// validate patch map
///*
pi = 0;
for (v = 0; v < numYTiles; v++) {
for (i = 0 ; i < numXTiles; i++) {
if (_patchMap[v][i] == null) throw new Error("Patch map not filled completely!");
patchList[pi++] = _patchMap[v][i];
}
}
pi = 0;
//*/
/*
for (var ver:Vertex = vertexList; ver = ver.next; ver != null) {
ver.u = (ver.x - boundMinX) * tw+0.0025;
ver.v = -(ver.y - boundMaxY) * th+0.0025;
}
*/
}
override alternativa3d function draw(camera:Camera3D, parentCanvas:Canvas):void {
var canvas:Canvas = parentCanvas.getChildCanvas(true, false, this, alpha, blendMode, colorTransform, filters);
super.draw(camera, parentCanvas);
var patch:Patch = curPatch; // patchList[pi];
if (patch == null) return;;
var aabb:Bounds3D = patch.aabb;
Debug.drawBounds(camera, canvas, this, aabb.boundMinX, aabb.boundMinY, aabb.boundMinZ, aabb.boundMaxX, aabb.boundMaxY, aabb.boundMaxZ, 0xFF0000, 1);
}
private var _appliedAllReady:Boolean = false;
public function applyAll(model:TileSetModel):void {
if (model.selectedTexture != null) {
var targTexture:TextureDefinition = model.selectedTexture;
var numYTiles:int = _patchMap.length;
var numXTiles:int = _patchMap[0].length;
var v:int; var i:int;
for (v = 0; v < numYTiles; v++) {
for (i = 0 ; i < numXTiles; i++) {
_patchMap[v][i].textureDef = targTexture;
}
}
_appliedAllReady = true;
}
}
public var excludedObjects:Dictionary = null;
private static const requestPatchTypes:Vector.<int> = new Vector.<int>(4);
private static const neighbourPaints:Vector.<PaintCommand> = new Vector.<PaintCommand>(8, true);
public function paintUsingRay(origin:Vector3D, direction:Vector3D, model:TileSetModel):RayIntersectionData {
if (!_appliedAllReady) return null;
var data:RayIntersectionData = intersectRay(origin, direction, excludedObjects);
if (data != null) {
var request:Vector.<int> = requestPatchTypes;
var selDef:TextureDefinition = model.selectedTexture;
if (selDef == null) return data;
var patch:Patch = data.face.id as Patch;
if (patch == null) return data;
var type:int = selDef.identityTypeLayout[0];
var xer:int = patch.xi;
var yer:int = patch.yi;
var xLen:int = _patchMap[0].length;
var yLen:int = _patchMap.length;
var tx:int;
var ty:int;
var applyDef:TextureDefinition;
var layout:Vector.<int>;
var count:int = 0;
// South bound
tx = xer - 1;
ty = yer - 1;
if (tx >= 0 && ty >= 0) {
patch = _patchMap[ty][tx];
layout = patch.textureDef.identityTypeLayout;
request[0] = layout[0];
request[1] = type;
request[2] = layout[2];
request[3] = layout[3];
applyDef = model.getDefinitionByCombination(request);
if (applyDef) {
neighbourPaints[count++] = new PaintCommand(patch, applyDef);
//patch.textureDef = applyDef;
}
}
tx = xer;
ty = yer - 1;
if (ty >= 0) {
patch = _patchMap[ty][tx];
layout = patch.textureDef.identityTypeLayout;
request[0] = type;
request[1] = type;
request[2] = layout[2];
request[3] = layout[3];
applyDef = model.getDefinitionByCombination(request);
if (applyDef) {
neighbourPaints[count++] = new PaintCommand(patch, applyDef);
//patch.textureDef = applyDef;
}
}
tx = xer + 1;
ty = yer - 1;
if (tx < yLen &&ty >=0) {
patch = _patchMap[ty][tx];
layout = patch.textureDef.identityTypeLayout;
request[0] = type;
request[1] = layout[1];
request[2] = layout[2];
request[3] = layout[3];
applyDef = model.getDefinitionByCombination(request);
if (applyDef) {
neighbourPaints[count++] = new PaintCommand(patch, applyDef);
//patch.textureDef = applyDef;
}
}
// West to East
tx = xer - 1;
ty = yer;
if (tx >= 0) {
patch = _patchMap[ty][tx];
layout = patch.textureDef.identityTypeLayout;
request[0] = layout[0];
request[1] = type;
request[2] = layout[2];
request[3] = type;
applyDef = model.getDefinitionByCombination(request);
if (applyDef) {
neighbourPaints[count++] = new PaintCommand(patch, applyDef);
//patch.textureDef = applyDef;
}
}
tx =xer + 1;
ty = yer;
if ( tx < xLen) {
patch = _patchMap[ty][tx];
layout = patch.textureDef.identityTypeLayout;
request[0] = type;
request[1] = layout[1];
request[2] = type;
request[3] = layout[3];
applyDef = model.getDefinitionByCombination(request);
if (applyDef) {
neighbourPaints[count++] = new PaintCommand(patch, applyDef);
//patch.textureDef = applyDef;
}
}
// North bound
tx =xer - 1;
ty =yer + 1;
if (tx >= 0 && ty < yLen) {
patch = _patchMap[ty][tx];
layout = patch.textureDef.identityTypeLayout;
request[0] = layout[0];
request[1] = layout[1];
request[2] = layout[2];
request[3] = type;
applyDef = model.getDefinitionByCombination(request);
if (applyDef) {
neighbourPaints[count++] = new PaintCommand(patch, applyDef);
//patch.textureDef = applyDef;
}
}
tx =xer
ty =yer + 1;
if (ty < yLen) {
patch = _patchMap[ty][tx];
layout = patch.textureDef.identityTypeLayout;
request[0] = layout[0];
request[1] = layout[1];
request[2] = type;
request[3] = type;
applyDef = model.getDefinitionByCombination(request);
if (applyDef) {
neighbourPaints[count++] =new PaintCommand(patch, applyDef);
//patch.textureDef = applyDef;
}
}
tx =xer + 1;
ty =yer + 1;
if (tx < xLen && ty < yLen) {
patch = _patchMap[ty][tx];
layout = patch.textureDef.identityTypeLayout;
request[0] = layout[0];
request[1] = layout[1];
request[2] = type;
request[3] = layout[3];
applyDef = model.getDefinitionByCombination(request);
if (applyDef) {
neighbourPaints[count++] = new PaintCommand(patch, applyDef);
//patch.textureDef = applyDef;
}
}
if (count == 8) {
patch = data.face.id as Patch;
patch.textureDef = selDef;
for (var i:int = 0; i < 8; i++) {
neighbourPaints[i].execute();
}
}
}
return data;
}
}
//}
//package utils.terrain
//{
import alternativ7.engine3d.core.Object3D;
import flash.geom.Vector3D;
/**
* Basic app wrapper to hold bounds 3d information.
* @author Glenn Ko
*/
//public
class Bounds3D
{
public var boundMinX:Number = 1.79e+308;
public var boundMaxX:Number = -1.79e+308;
public var boundMinY:Number = 1.79e+308;
public var boundMaxY:Number = -1.79e+308;
public var boundMinZ:Number = 1.79e+308;
public var boundMaxZ:Number = -1.79e+308;
public function Bounds3D() {
}
public function writeTo(obj:Object3D):void {
obj.boundMinX = boundMinX;
obj.boundMinY = boundMinY;
obj.boundMinZ = boundMinZ;
obj.boundMaxX = boundMaxX;
obj.boundMaxY = boundMaxY;
obj.boundMaxZ = boundMaxZ;
}
public function isValid():Boolean {
return !( isNaN(boundMaxX) || isNaN(boundMaxY) || isNaN(boundMaxZ) || isNaN(boundMinX) || isNaN(boundMinY) || isNaN(boundMinZ) );
}
public function getWidth():Number
{
return boundMaxX - boundMinX;
}
public function getHeight():Number
{
return boundMaxY - boundMinY;
}
public function calculateCenter(output:Vector3D):void {
output.x = boundMinX + (boundMaxX - boundMinX) * .5;
output.y = boundMinY + (boundMaxY - boundMinY) * .5;
output.z = boundMinZ + (boundMaxZ - boundMinZ) * .5;
}
public function union(target:Bounds3D):void {
if (target.boundMinX < boundMinX) boundMinX = target.boundMinX;
if (target.boundMaxX > boundMaxX) boundMaxX = target.boundMaxX;
if (target.boundMinY < boundMinY) boundMinY = target.boundMinY;
if (target.boundMaxY > boundMaxY) boundMaxY = target.boundMaxY;
if (target.boundMinZ < boundMinZ) boundMinZ = target.boundMinZ;
if (target.boundMaxZ > boundMaxZ) boundMaxZ = target.boundMaxZ;
}
public function toString():String {
return "Bounds3D:: min/max: "+new Vector3D(boundMinX, boundMinY, boundMinZ) + ", "+ new Vector3D(boundMaxX, boundMaxY, boundMaxZ);
}
}
//}
//package utils.terrain
//{
import alternativ7.engine3d.core.Face;
import alternativ7.engine3d.core.Vertex;
use namespace alternativa3d;
/**
* A patch is a square tile definition consisting of 2 triangular faces
* @author Glenn Ko
*/
//public
class Patch
{
public var tri1:Face;
public var tri2:Face;
public var aabb:Bounds3D;
// tile index
public var yi:int;
public var xi:int;
private var _textureDef:TextureDefinition;
public function Patch(tri1:Face, tri2:Face)
{
this.tri1 = tri1;
this.tri2 = tri2;
tri1.id = this;
tri2.id = this;
aabb = new Bounds3D();
calculateAABBForTri(tri1);
calculateAABBForTri(tri2);
tri1.wrapper = FaceMacros.deepCloneWrapperAndVertices(tri1.wrapper);
tri2.wrapper = FaceMacros.deepCloneWrapperAndVertices(tri2.wrapper);
}
public function calculateUVs():void {
var v:Vertex;
v = tri1.wrapper.vertex;
v.u = int(v.x- aabb.boundMinX) >> 7;
v.v = int(v.y - aabb.boundMinY) >> 7; v.v = 1 - v.v;
v = tri1.wrapper.next.vertex;
v.u = int(v.x- aabb.boundMinX) >> 7;
v.v = int(v.y- aabb.boundMinY) >> 7; v.v = 1 - v.v;
v = tri1.wrapper.next.next.vertex;
v.u = int(v.x- aabb.boundMinX) >> 7;
v.v = int(v.y - aabb.boundMinY) >> 7; v.v = 1 - v.v;
v = tri2.wrapper.vertex;
v.u = int(v.x- aabb.boundMinX) >> 7;
v.v = int(v.y - aabb.boundMinY) >> 7; v.v = 1 - v.v;
v = tri2.wrapper.next.vertex;
v.u = int(v.x- aabb.boundMinX) >> 7;
v.v = int(v.y- aabb.boundMinY) >> 7; v.v = 1 - v.v;
v = tri2.wrapper.next.next.vertex;
v.u = int(v.x- aabb.boundMinX) >> 7;
v.v = int(v.y- aabb.boundMinY) >> 7; v.v = 1 - v.v;
}
private function calculateAABBForTri(tri:Face):void {
var xer:Number;
var v:Vertex;
v = tri.wrapper.vertex;
xer = v.x;
if (xer < aabb.boundMinX) aabb.boundMinX = xer;
if (xer > aabb.boundMaxX) aabb.boundMaxX = xer;
xer = v.y;
if (xer < aabb.boundMinY) aabb.boundMinY = xer;
if (xer > aabb.boundMaxY) aabb.boundMaxY = xer;
xer = v.z;
if (xer < aabb.boundMinZ) aabb.boundMinZ = xer;
if (xer > aabb.boundMaxZ) aabb.boundMaxZ = xer;
v = tri.wrapper.next.vertex;
xer = v.x;
if (xer < aabb.boundMinX) aabb.boundMinX = xer;
if (xer > aabb.boundMaxX) aabb.boundMaxX = xer;
xer = v.y;
if (xer < aabb.boundMinY) aabb.boundMinY = xer;
if (xer > aabb.boundMaxY) aabb.boundMaxY = xer;
xer = v.z;
if (xer < aabb.boundMinZ) aabb.boundMinZ = xer;
if (xer > aabb.boundMaxZ) aabb.boundMaxZ = xer;
v = tri.wrapper.next.next.vertex;
xer = v.x;
if (xer < aabb.boundMinX) aabb.boundMinX = xer;
if (xer > aabb.boundMaxX) aabb.boundMaxX = xer;
xer = v.y;
if (xer < aabb.boundMinY) aabb.boundMinY = xer;
if (xer > aabb.boundMaxY) aabb.boundMaxY = xer;
xer = v.z;
if (xer < aabb.boundMinZ) aabb.boundMinZ = xer;
if (xer > aabb.boundMaxZ) aabb.boundMaxZ = xer;
calculateUVs();
}
public function get textureDef():TextureDefinition { return _textureDef; }
public function set textureDef(value:TextureDefinition):void
{
_textureDef = value;
tri1.material = _textureDef.material;
tri2.material = _textureDef.material;
_textureDef.material.repeat = false;
}
}
//}
//package portal
//{
import alternativ7.engine3d.core.Face;
import alternativ7.engine3d.alternativa3d;
import alternativ7.engine3d.core.Vertex;
import alternativ7.engine3d.core.Wrapper;
import alternativ7.engine3d.objects.Mesh;
use namespace alternativa3d;
/**
* ...
* @author Glenn Ko
*/
//public
class FaceMacros
{
public static function deepCloneWrapperAndVertices(wrapper:Wrapper):Wrapper {
var wrapperClone:Wrapper = wrapper.create();
var v:Vertex = wrapper.vertex.create();
v.x = wrapper.vertex.x;
v.y = wrapper.vertex.y;
v.z = wrapper.vertex.z;
v.u = wrapper.vertex.u;
v.v = wrapper.vertex.v;
wrapperClone.vertex = v;
var w:Wrapper = wrapper.next;
var tailWrapper:Wrapper = wrapperClone;
var wClone:Wrapper;
while (w != null) {
wClone = w.create();
v = w.vertex.create();
v.x = w.vertex.x;
v.y = w.vertex.y;
v.z = w.vertex.z;
v.u = w.vertex.u;
v.v = w.vertex.v;
wClone.vertex = v;
tailWrapper.next = wClone;
tailWrapper = wClone;
w = w.next;
}
return wrapperClone;
}
}
//}
//package portal
//{
import alternativ7.engine3d.alternativa3d;
import alternativ7.engine3d.materials.FillMaterial;
import alternativ7.engine3d.materials.Material;
import alternativ7.engine3d.primitives.Box;
/**
* ...
* @author Glenn Ko
*/
//public
class MarkerBox extends Box
{
public function MarkerBox(color:uint = 0x00FF00, width:Number = 10, length:Number = 10, height:Number = 10, widthSegments:uint = 1, lengthSegments:uint = 1, heightSegments:uint = 1, reverse:Boolean = false, triangulate:Boolean = false, left:Material = null, right:Material = null, back:Material = null, front:Material = null, bottom:Material = null, top:Material = null)
{
var material:Material = new FillMaterial(color);
super(width, length, height, widthSegments, lengthSegments, heightSegments, reverse, triangulate, material, material, material, material, material, material);
}
}
//}