forked from: TileIndexMaterialTest
forked from TileIndexMaterialTest (diff: 159)
ActionScript3 source code
/**
* Copyright Glidias ( http://wonderfl.net/user/Glidias )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/7xeb
*/
package {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.materials.TextureMaterial;
import alternativa.engine3d.materials.VertexLightTextureMaterial;
import alternativa.engine3d.materials.StandardMaterial;
import alternativa.engine3d.objects.WireFrame;
import alternativa.engine3d.objects.Mesh
import alternativa.engine3d.primitives.GeoSphere;
import alternativa.engine3d.primitives.Plane;
import alternativa.engine3d.resources.BitmapTextureResource;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.geom.Point;
import flash.net.URLRequest;
import flash.system.LoaderContext;
use namespace alternativa3d;
public class TileIndexMaterialTest extends Sprite {
private var scene:Template
private const RADIAN:Number = Math.PI / 180;
private var atlasNoTilesH:int = 2;
private var atlasTilePadding:int = 0;
private var TILE_SIZE_PX:int = 128;
public var mipmaps:Mipmaps = new Mipmaps();
private function getPositionAtIndex(index:int):Object {
// return { x:0, y:0 };
var colIndex:int = (index % atlasNoTilesH);
var rowIndex:int = int(index / atlasNoTilesH);
colIndex *= (TILE_SIZE_PX*2 + atlasTilePadding * 2);
colIndex += atlasTilePadding;
rowIndex *= (TILE_SIZE_PX + atlasTilePadding * 2);
rowIndex += atlasTilePadding;
return {x: (colIndex > 0 ? colIndex : colIndex ), y:(rowIndex > 0 ? rowIndex : rowIndex)};
}
private function compose4x4ColorTest(ru:Number, rv:Number):uint {
return ( (uint(ru * 4) << 22) | (uint(rv * 4) << 20) );
}
private function compose4x4(ru:Number, rv:Number, gu:Number, gv:Number, ma:int, mb:int, mc:int, md:int):uint {
return ( (uint(ru * 4) << 22) | (uint(rv * 4) << 20) );
}
public function TileIndexMaterialTest():void {
// preload crap before init
var loader:Loader = new Loader();
loader.load( new URLRequest("http://glidias.freehostia.com/getimage.php?url=tribes/textures/lushdml/lcccc.jpg"), new LoaderContext(true) );
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadTexture1Done);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onIOerror);
}
private function loadTexture1Done(e:Event):void {
texture2 = ((e.currentTarget as LoaderInfo).content as Bitmap).bitmapData;
var loader:Loader = new Loader();
loader.load( new URLRequest("http://glidias.freehostia.com/getimage.php?url=tribes/textures/lushdml/lffff.jpg"), new LoaderContext(true) );
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onIOerror);
}
private function onIOerror(e:IOErrorEvent):void
{
ATLAS = new Bitmap( new BitmapData(128, 128,false, 0xFF0000) );
init();
}
private var ATLAS:Bitmap;
private var texture1:BitmapData;
private var texture2:BitmapData;
private function onLoadComplete(e:Event):void
{
texture1 = ((e.currentTarget as LoaderInfo).content as Bitmap).bitmapData;
ATLAS = new Bitmap( new BitmapData(texture1.width * 2 + texture2.width * 2, texture1.height*2, false, 0) );
init();
}
private function init():void {
//texture1.fillRect(texture1.rect, 0xFF0000);
//texture2.fillRect(texture2.rect, 0x00FF00);
scene = new Template();
scene.addEventListener(Template.VIEW_CREATE, initialize);
addChild(scene);
}
private function RGBToHex(r:int, g:int, b:int):uint
{
var hex:uint = ( r << 16 ) | ( g << 8 ) | b;
return hex;
}
private var paddData:BitmapData = new BitmapData(1, 128, false, 0);
private function initialize(event:Event):void {
scene.removeEventListener(Template.VIEW_CREATE, initialize);
//Textureの作成
var diffuseMap:BitmapData = ATLAS.bitmapData;
var pt:Point = new Point();
diffuseMap.copyPixels( texture1, texture1.rect, pt ); //new BitmapData(ATLAS, 16, false, 0xFF6666);
pt.x += 128;
mipmaps.texture = texture1;
// duplication of next texture to avoid back-push seams, before pasting over mipmaps
// diffuseMap.copyPixels( texture2, texture2.rect, pt );
// pixel extrude version instead
/*
paddData.copyPixels(texture2, paddData.rect, new Point(0, 0));
for (var i:int = 0; i < 128; i++) {
diffuseMap.copyPixels(paddData, paddData.rect, new Point(pt.x + i, pt.y));
}
*/
//diffuseMap.copyPixels(paddData, paddData.rect, new Point(pt.x - 1, pt.y));
//diffuseMap.copyPixels(paddData, paddData.rect, new Point(pt.x - 2, pt.y));
mipmaps.pasteMipmaps(diffuseMap, pt);
pt.x += 128;
diffuseMap.copyPixels( texture2, texture2.rect, pt ); //new BitmapData(ATLAS, 16, false, 0xFF6666);
pt.x += 128;
mipmaps.texture = texture2;
mipmaps.pasteMipmaps(diffuseMap, pt);
pt.y = 128;
pt.x = 0;
// pt.x += 128;
diffuseMap.copyPixels( texture2, texture2.rect, pt ); //new BitmapData(ATLAS, 16, false, 0xFF6666);
pt.x += 128;
mipmaps.texture = texture2;
// duplication of next texture to avoid back-push seams, before pasting over mipmaps
// diffuseMap.copyPixels( texture1, texture1.rect, pt );
mipmaps.pasteMipmaps(diffuseMap, pt);
pt.x += 128;
diffuseMap.copyPixels( texture1, texture1.rect, pt );
mipmaps.texture = texture1;
pt.x += 128;
mipmaps.pasteMipmaps(diffuseMap, pt);
var normalMap:BitmapData = new BitmapData(16, 16, false, 0x8080FF);
var tileSizePx:int = TILE_SIZE_PX;
// throw new Error(compose4x4ColorTest(1 / 2, 1 / 2) );
var tileMap:BitmapData = new BitmapData(4, 4, false, compose4x4ColorTest(1/2, 1/2) );
tileMap.setPixel(1, 0, compose4x4ColorTest(1/2, 0/2) );
tileMap.setPixel(3, 2, compose4x4ColorTest(0/2, 1/2) );
tileMap.setPixel(1,2, compose4x4ColorTest(0/2, 1/2) );
//マテリアルの作成
var bitmapResource:BitmapTextureResource = new BitmapTextureResource(diffuseMap);
var normalResource:BitmapTextureResource = new BitmapTextureResource(normalMap);
var tileResource:BitmapTextureResource = new BitmapTextureResource(tileMap);
var materialA:TextureMaterial = new StandardMaterial(bitmapResource, normalResource);
var materialB:TextureMaterial = new TextureMaterial(normalResource);
var material:TileIndexMaterial = new TileIndexMaterial(bitmapResource, new BitmapTextureResource(mipmaps.getMipmapOffsetTable()), mipmaps.mipmapUVCap, new <BitmapTextureResource>[tileResource], normalResource, new <BitmapTextureResource>[null], null, tileSizePx, 0);
material.glossiness = 0;
material.specularPower = 0;
material.resolution = 1; // adjust as you see fit
addChild( new Bitmap(diffuseMap) );
var plane:Plane = new Plane(tileMap.width*256, tileMap.height*256, 1, 1, true, false, material, material);
scene.controlObject.addChild(plane)
scene.initialize();
}
}
}
class TextureAtlasData extends flash.display.BitmapData {
private var bakeMipmaps:Boolean;
private var numTilesHorizontal:int;
private var numTilesVertical:int;
private var tileSize:int;
function TextureAtlasData(tileSize:int, numTilesHorizontal:int, numTilesVertical:int, bakeMipmaps:Boolean=true) {
super(numTilesHorizontal * tileSize * (bakeMipmaps ? 2 :1), numTilesVertical * tileSize, false, 0);
this.tileSize = tileSize;
this.numTilesVertical = numTilesVertical;
this.numTilesHorizontal = numTilesHorizontal;
this.bakeMipmaps = bakeMipmaps;
}
public function loadURLs(fileList:Array):void {
var len:int = fileList.length;
for (var i:int = 0; i < len; i++) {
}
}
}
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.primitives.GeoSphere;
import alternativa.engine3d.controllers.SimpleObjectController;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Resource;
import alternativa.engine3d.core.View;
import alternativa.engine3d.core.VertexAttributes;
import alternativa.engine3d.core.Transform3D;
import flash.display.BitmapData;
import flash.filters.ConvolutionFilter;
import alternativa.engine3d.objects.Mesh;
import alternativa.engine3d.objects.Surface;
import alternativa.engine3d.objects.Joint;
import alternativa.engine3d.lights.AmbientLight;
import alternativa.engine3d.lights.DirectionalLight;
import alternativa.engine3d.resources.Geometry;
import flash.display.Sprite;
import flash.display.Stage3D;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.utils.Dictionary;
import flash.geom.Vector3D;
import flash.utils.Dictionary;
import flash.geom.*;
class Template extends Sprite {
public static const VIEW_CREATE:String = 'view_create'
private var stage3D:Stage3D
private var camera:Camera3D
public var scene:Object3D
public var cameraController:SimpleObjectController;
public var objectController:SimpleObjectController;
public var controlObject:Object3D;
protected var directionalLight:DirectionalLight;
protected var ambientLight:AmbientLight;
public function Template() {
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
stage.quality = StageQuality.HIGH;
//Stage3Dを用意
stage3D = stage.stage3Ds[0];
//Context3Dの生成、呼び出し、初期化
stage3D.addEventListener(Event.CONTEXT3D_CREATE, onContextCreate);
stage3D.requestContext3D();
}
private function onContextCreate(e:Event):void {
stage3D.removeEventListener(Event.CONTEXT3D_CREATE, onContextCreate);
//View3D(表示エリア)の作成
var view:View = new View(stage.stageWidth, stage.stageHeight);
view.antiAlias = 4
addChild(view);
//Scene(コンテナ)の作成
scene = new Object3D();
//Camera(カメラ)の作成
camera = new Camera3D(1, 100000);
camera.view = view;
scene.addChild(camera)
camera.diagram
addChild(camera.diagram)
//Cameraをコントロールする場合は、CameraControlerの作成
cameraController = new SimpleObjectController(stage, camera, 100);
cameraController.mouseSensitivity = 0;
cameraController.unbindAll();
//Cameraの位置調整
cameraController.setObjectPosXYZ(0, -300, 0);
cameraController.lookAtXYZ(0, 0, 0);
//Lightを追加
ambientLight = new AmbientLight(0xFFFFFF);
ambientLight.intensity = 0.5;
scene.addChild(ambientLight);
//Lightを追加
directionalLight = new DirectionalLight(0xFFFFFF);
//手前右上から中央へ向けた指向性light
directionalLight.x = 0;
directionalLight.y = -100;
directionalLight.z = -100;
directionalLight.lookAt(0, 0, 0);
scene.addChild(directionalLight);
//directionalLight.visible = false;
//コントロールオブジェクトの作成
controlObject = new Object3D()
scene.addChild(controlObject);
dispatchEvent(new Event(VIEW_CREATE));
}
public function initialize():void {
for each (var resource:Resource in scene.getResources(true)) {
trace(resource)
resource.upload(stage3D.context3D);
}
//オブジェクト用のコントローラー(マウス操作)
objectController = new SimpleObjectController(stage, controlObject, 100);
objectController.mouseSensitivity = 0.2;
//レンダリング
camera.render(stage3D);
addEventListener(Event.ENTER_FRAME, onRenderTick);
}
public function onRenderTick(e:Event):void {
objectController.update()
camera.render(stage3D);
}
}
/**
* Primitive
*/
class Primitive extends Mesh {
protected const RADIAN:Number = Math.PI / 180;
public var inSide:Surface = null;
public var outSide:Surface = null;
protected var indices:Vector.<uint> = new Vector.<uint>();
protected var positions:Vector.<Number> = new Vector.<Number>();
protected var texcoords:Vector.<Number> = new Vector.<Number>();
//表用と裏用のpositionsとtexcoordsとindicesとを追加
protected var positionsInSide:Vector.<Number> = new Vector.<Number>();
protected var positionsOutSide:Vector.<Number> = new Vector.<Number>();
protected var texcoordsInSide:Vector.<Number> = new Vector.<Number>();
protected var texcoordsOutSide:Vector.<Number> = new Vector.<Number>();
protected var indicesInSide:Vector.<uint> = new Vector.<uint>();
protected var indicesOutSide:Vector.<uint> = new Vector.<uint>();
public function Primitive() {
geometry = new Geometry();
var pos:int = VertexAttributes.POSITION
var tex:int = VertexAttributes.TEXCOORDS[0]
geometry.addVertexStream([pos,pos,pos,tex,tex]);
}
protected function setGeometry():void {
positions = positionsOutSide.concat(positionsInSide);
texcoords = texcoordsOutSide.concat(texcoordsInSide);
indices = indicesOutSide.concat();
var indexStart:int = positionsOutSide.length / 3;
var count:uint = indicesInSide.length;
for (var i:uint = 0; i < count; i++) {
indices.push(indicesInSide[i] + indexStart);
}
geometry.numVertices = positions.length / 3;
geometry.setAttributeValues(VertexAttributes.POSITION, positions);
geometry.setAttributeValues(VertexAttributes.TEXCOORDS[0], texcoords);
geometry.indices = indices;
if (indicesInSide.length) {
inSide = this.addSurface(null, indicesOutSide.length, indicesInSide.length / 3);
}
if (indicesOutSide.length) {
outSide = this.addSurface(null, 0, indicesOutSide.length / 3);
}
MeshUtility.createNormal(this);
}
}
class RoundMesh extends Primitive {
public function RoundMesh(lineList:Vector.<Point>, radialSegments:uint = 3,lastSegments:uint = 0,star:Number = 0, twoSide:Boolean = false, reverse:Boolean = false) {
super();
var rInterval:Number = 360 / radialSegments;
var heightSegments:uint = lineList.length - 1;
var height:Number = lineList[heightSegments].y - lineList[0].y;
var radian:Number
var segmentCount:int = (lastSegments > 0) ? lastSegments : radialSegments;
for (var i:int = 0; i < segmentCount; i++) {
for (var j:int = 0; j < heightSegments; j++) {
var vertices:Vector.<Vector3D> = new Vector.<Vector3D>(4);
var uvs:Vector.<Point> = new Vector.<Point>(4);
//時計周りで四角を作成していく
var tempRadiusA:Number = (i % 2 == 1 && star > 0) ? lineList[j].x * star : lineList[j].x
var tempRadiusB:Number = (i % 2 == 1 && star > 0) ? lineList[j+1].x * star : lineList[j+1].x
radian = (rInterval * i - 90) * RADIAN;
vertices[0] = new Vector3D(Math.cos(radian) * tempRadiusA, Math.sin(radian) * tempRadiusA, lineList[j].y-(height/2));
vertices[3] = new Vector3D(Math.cos(radian) * tempRadiusB, Math.sin(radian) * tempRadiusB, lineList[j+1].y-(height/2));
radian = (rInterval * (i + 1) - 90) * RADIAN;
vertices[1] = new Vector3D(Math.cos(radian) * tempRadiusA, Math.sin(radian) * tempRadiusA, lineList[j].y-(height/2));
vertices[2] = new Vector3D(Math.cos(radian) * tempRadiusB, Math.sin(radian) * tempRadiusB, lineList[j+1].y-(height/2));
uvs[0] = new Point(1 / radialSegments * -i, 1 / heightSegments * j);
uvs[3] = new Point(1 / radialSegments * -i, 1 / heightSegments * (j+1));
uvs[1] = new Point(1 / radialSegments * -(i+1), 1 / heightSegments * j);
uvs[2] = new Point(1 / radialSegments * -(i+1), 1 / heightSegments * (j+1));
//Cylinderに巻きつける場合、裏表が逆になるので注意(UVも逆)
if (reverse == false || twoSide == true) {
MeshUtility.createSquare(vertices, uvs, indicesOutSide, positionsOutSide, texcoordsOutSide, true)
}
if (reverse == true || twoSide == true) {
MeshUtility.createSquare(vertices, uvs, indicesInSide, positionsInSide, texcoordsInSide, false)
}
}
}
setGeometry();
}
}
class Cylinder extends Primitive {
public function Cylinder(topRadius:Number = 0, bottomRadius:Number = 50, height:Number = 100, radialSegments:uint = 3, heightSegments:uint = 1, lastSegments:uint = 0,star:Number = 0, twoSide:Boolean = false, reverse:Boolean = false) {
var rInterval:Number = 360 / radialSegments;
var hInterval:Number = height / heightSegments;
var radian:Number
var segmentCount:int = (lastSegments > 0) ? lastSegments : radialSegments;
for (var i:int = 0; i < segmentCount; i++) {
for (var j:int = 0; j < heightSegments; j++) {
var vertices:Vector.<Vector3D> = new Vector.<Vector3D>(4);
var uvs:Vector.<Point> = new Vector.<Point>(4);
var px:Number=0;
var py:Number = 0;
var tmpR:Number = 0;
var starRatio:Number = 0;
radian = (rInterval * i - 90) * RADIAN;
starRatio = (i % 2 == 1 && star > 0) ? star : 1;
px = Math.cos(radian)
py = Math.sin(radian)
tmpR = getRadius(topRadius, bottomRadius, height, hInterval * j, starRatio);
vertices[0] = new Vector3D(px * tmpR, py * tmpR, hInterval * j - (height / 2));
tmpR = getRadius(topRadius, bottomRadius, height, hInterval * (j + 1), starRatio);
vertices[3] = new Vector3D(px * tmpR, py * tmpR, hInterval * (j + 1) - (height / 2));
radian = (rInterval * (i + 1) - 90) * RADIAN;
starRatio = (i % 2 == 0 && star > 0) ? star : 1;
px = Math.cos(radian);
py = Math.sin(radian);
tmpR = getRadius(topRadius, bottomRadius, height, hInterval * j, starRatio);
vertices[1] = new Vector3D(px * tmpR, py * tmpR, hInterval * j - (height / 2));
tmpR = getRadius(topRadius, bottomRadius, height, hInterval * (j + 1), starRatio);
vertices[2] = new Vector3D(px * tmpR, py * tmpR, hInterval * (j + 1) - (height / 2));
var u:Number = 1 / radialSegments;
var v:Number = 1 / heightSegments;
uvs[0] = new Point(u * -i, v * j);
uvs[3] = new Point(u * -i, v * (j + 1));
uvs[1] = new Point(u * -(i + 1), v * j);
uvs[2] = new Point(u * -(i + 1), v * (j + 1));
//Cylinderに巻きつける場合、裏表が逆になるので注意(UVも逆)
if (reverse == false || twoSide == true) {
MeshUtility.createSquare(vertices, uvs, indicesOutSide, positionsOutSide, texcoordsOutSide, true)
}
if (reverse == true || twoSide == true) {
MeshUtility.createSquare(vertices, uvs, indicesInSide, positionsInSide, texcoordsInSide, false)
}
}
}
setGeometry()
}
/**
* 半径を高さの比率から割り出す
* @return
*/
private function getRadius(topRadius:Number, bottomRadius:Number, height:Number, length:Number, starRatio:Number):Number {
var result:Number
var difference:Number
var ratio:Number
if (topRadius > bottomRadius) {
difference = topRadius - bottomRadius;
ratio = (length) ? (height - length) / height : 1
result = (bottomRadius + (difference * ratio));
} else {
difference = bottomRadius - topRadius;
ratio = (length) ? length / height : 0
result = (topRadius + (difference * ratio));
}
result *= starRatio
return result;
}
}
/**
* 頂点に隣接する面法線を収集するクラス
*/
class ExtraVertex {
public var vertex:Vector3D;
public var normals:Dictionary;
public var indices:Vector.<uint>;
public function ExtraVertex(x:Number, y:Number, z:Number) {
vertex = new Vector3D(x, y, z);
normals = new Dictionary();
indices = new Vector.<uint>();
}
}
/**
* MeshUtility
*/
use namespace alternativa3d;
class MeshUtility {
public function MeshUtility() {
}
/**
* 3つの頂点座標から、Faceを作成し、indices、positionsに各値を登録する
*/
public static function createTriangle(vertices:Vector.<Vector3D>, uvs:Vector.<Point>,
indices:Vector.<uint>, positions:Vector.<Number>,
texcoords:Vector.<Number>, reverse:Boolean = false):void {
if (reverse == false) {
//三角形用の頂点を登録
positions.push(vertices[0].x, vertices[0].y, vertices[0].z,
vertices[2].x, vertices[2].y, vertices[2].z,
vertices[1].x, vertices[1].y, vertices[1].z);
//三角形用のUVを登録
texcoords.push(uvs[0].x, uvs[0].y,uvs[2].x, uvs[2].y,uvs[1].x, uvs[1].y);
} else {
//三角形用の頂点を登録
positions.push(vertices[0].x, vertices[0].y, vertices[0].z,
vertices[1].x, vertices[1].y, vertices[1].z,
vertices[2].x, vertices[2].y, vertices[2].z);
//三角形用のUVを登録
texcoords.push(uvs[0].x, uvs[0].y,uvs[1].x, uvs[1].y,uvs[2].x, uvs[2].y);
}
//Face用indexを登録
var startIndex:uint = indices.length
indices.push(startIndex + 0, startIndex + 1, startIndex + 2);
}
/**
* 4つの頂点座標から、四角形(2つのFace)を作成し、indices、positions、uvsに各値を登録する
*/
public static function createSquare(vertices:Vector.<Vector3D>, uvs:Vector.<Point>,
indices:Vector.<uint>, positions:Vector.<Number>,
texcoords:Vector.<Number>, reverse:Boolean = false):void {
if (reverse == false) {
positions.push( vertices[0].x, vertices[0].y, vertices[0].z,
vertices[3].x, vertices[3].y, vertices[3].z,
vertices[1].x, vertices[1].y, vertices[1].z);
positions.push( vertices[1].x, vertices[1].y, vertices[1].z,
vertices[3].x, vertices[3].y, vertices[3].z,
vertices[2].x, vertices[2].y, vertices[2].z);
//三角形用のUVを登録
texcoords.push(uvs[0].x, uvs[0].y,uvs[3].x, uvs[3].y,uvs[1].x, uvs[1].y);
texcoords.push(uvs[1].x, uvs[1].y,uvs[3].x, uvs[3].y,uvs[2].x, uvs[2].y);
} else {
positions.push( vertices[0].x, vertices[0].y, vertices[0].z,
vertices[1].x, vertices[1].y, vertices[1].z,
vertices[3].x, vertices[3].y, vertices[3].z);
positions.push( vertices[1].x, vertices[1].y, vertices[1].z,
vertices[2].x, vertices[2].y, vertices[2].z,
vertices[3].x, vertices[3].y, vertices[3].z);
//三角形用のUVを登録
texcoords.push(uvs[0].x, uvs[0].y,uvs[1].x, uvs[1].y,uvs[3].x, uvs[3].y);
texcoords.push(uvs[1].x, uvs[1].y, uvs[2].x, uvs[2].y, uvs[3].x, uvs[3].y);
}
//Face用indexを登録
var startIndex:uint = indices.length
indices.push(startIndex + 0, startIndex + 1, startIndex + 2);
indices.push(startIndex + 3, startIndex + 4, startIndex + 5);
}
/**
* 指定Meshの法線を作成します
* @param mesh
*/
public static function createNormal(mesh:Mesh):void {
//法線の有無のチェック
if (mesh.geometry.hasAttribute(VertexAttributes.NORMAL) == false) {
var nml:int = VertexAttributes.NORMAL;
mesh.geometry.addVertexStream([nml,nml,nml]);
}
var indices:Vector.<uint> = mesh.geometry.indices;
var positions:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.POSITION);
var vartices:Vector.<Vector3D> = new Vector.<Vector3D>(positions.length / 3);
var vNormals:Vector.<Vector3D> = new Vector.<Vector3D>(positions.length / 3);
var i:int;
var count:uint = positions.length / 3;
for (i = 0; i < count; i++) {
vartices[i] = new Vector3D(positions[i * 3], positions[i * 3 + 1], positions[i * 3 + 2]);
}
//面法線を求め、頂点法線に代入する
count = indices.length;
for (i = 0; i < count; i += 3) {
var normal:Vector3D = calcNormal(vartices[indices[i]], vartices[indices[i + 1]], vartices[indices[i + 2]]);
vNormals[indices[i]] = normal;
vNormals[indices[i + 1]] = normal;
vNormals[indices[i + 2]] = normal;
}
var normals:Vector.<Number> = new Vector.<Number>();
count = vNormals.length;
for (i = 0; i < count; i++) {
if (vNormals[i]) {
normals.push(vNormals[i].x, vNormals[i].y, vNormals[i].z);
}
}
mesh.geometry.setAttributeValues(VertexAttributes.NORMAL, normals);
}
/**
* 面法線の計算
* 三つの頂点座標からなる三角ポリゴンの法線を計算し返します
*/
public static function calcNormal(a:Vector3D, b:Vector3D, c:Vector3D):Vector3D {
var v1:Vector3D = b.subtract(a);
var v2:Vector3D = c.subtract(a);
var v3:Vector3D = v1.crossProduct(v2);
//var v3:Vector3D = cross(v1,v2);
v3.normalize();
return (v3);
}
/**
* 指定Meshの法線をSmoothShadingにします
* @param mesh
*/
public static function smoothShading(mesh:Mesh,separateSurface:Boolean=false,threshold:Number=0.000001):void {
var indices:Vector.<uint> = mesh.geometry.indices;
var positions:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.POSITION);
var normals:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.NORMAL);
var vartices:Vector.<Vector3D> = new Vector.<Vector3D>(positions.length / 3);
var vNormals:Vector.<Vector3D> = new Vector.<Vector3D>(normals.length / 3);
var vertexDictionary:Dictionary = new Dictionary()
var exVertex:ExtraVertex;
//サーフェースごとに判断する
for (var s:uint = 0; s < mesh.numSurfaces; s++ ) {
var side:String = (separateSurface) ? s.toString() : '';
for (var n:uint = 0; n < mesh.getSurface(s).numTriangles * 3; n++) {
var i:uint = indices[n+mesh.getSurface(s).indexBegin];
vartices[i] = new Vector3D(positions[i * 3], positions[i * 3 + 1], positions[i * 3 + 2]);
//誤差を丸める
vartices[i].x = int(vartices[i].x / threshold) * threshold;
vartices[i].y = int(vartices[i].y / threshold) * threshold;
vartices[i].z = int(vartices[i].z / threshold) * threshold;
vNormals[i] = new Vector3D(normals[i * 3], normals[i * 3 + 1], normals[i * 3 + 2]);
//誤差を丸める
vNormals[i].x = int(vNormals[i].x / threshold) * threshold;
vNormals[i].y = int(vNormals[i].y / threshold) * threshold;
vNormals[i].z = int(vNormals[i].z / threshold) * threshold;
//同じ頂点を集める
//ただし、表裏がある場合があるので法線の方向もチェックする
if (vertexDictionary[vartices[i].toString()+'_'+side]) {
exVertex = vertexDictionary[vartices[i].toString()+'_'+side]
if (exVertex.normals[vNormals[i].toString()+'_'+side] == null) {
exVertex.normals[vNormals[i].toString()+'_'+side] = vNormals[i];
}
exVertex.indices.push(i);
} else {
exVertex = new ExtraVertex(vNormals[i].x, vNormals[i].y, vNormals[i].z);
exVertex.normals[vNormals[i].toString()+'_'+side] = vNormals[i];
exVertex.indices.push(i)
vertexDictionary[vartices[i].toString()+'_'+side] = exVertex
}
}
}
//Normalの平均化
var count:uint = 0;
for each (exVertex in vertexDictionary) {
var normalX:Number = 0;
var normalY:Number = 0;
var normalZ:Number = 0;
count = 0
for each (var normal:Vector3D in exVertex.normals) {
normalX += normal.x;
normalY += normal.y;
normalZ += normal.z;
count++
}
normal = new Vector3D(normalX / count, normalY / count, normalZ / count);
normal.normalize();
count = exVertex.indices.length;
for (i = 0; i < count; i++) {
vNormals[exVertex.indices[i]] = normal;
}
}
count = vNormals.length;
normals = new Vector.<Number>();
for (i = 0; i < count; i++) {
normals.push(vNormals[i].x, vNormals[i].y, vNormals[i].z);
}
mesh.geometry.setAttributeValues(VertexAttributes.NORMAL, normals);
}
/**
* 指定MeshのUVをVertexのxyから仮に作成する
* @param mesh
*/
public static function createUv(mesh:Mesh):void {
if (mesh.geometry.hasAttribute(VertexAttributes.TEXCOORDS[0]) == false) {
var tex:int = VertexAttributes.TEXCOORDS[0];
mesh.geometry.addVertexStream([tex,tex]);
}
mesh.calculateBoundBox()
var width:Number = mesh.boundBox.maxX - mesh.boundBox.minX
var length:Number = mesh.boundBox.maxZ - mesh.boundBox.minZ
var positions:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.POSITION);
var texcoords:Vector.<Number> = new Vector.<Number>;
var i:int;
for (i = 0; i < positions.length; i += 3) {
texcoords.push((positions[i] - mesh.boundBox.minX) / width, (positions[i + 2] - mesh.boundBox.minZ) / length);
}
mesh.geometry.setAttributeValues(VertexAttributes.TEXCOORDS[0], texcoords);
}
/**
* 指定MeshのTangentを作成する
* @param mesh
*/
static public function createTangent(mesh:Mesh):void {
//接線有無のチェック
var positions:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.POSITION);
var texcoords:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.TEXCOORDS[0]);
var normals:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.NORMAL);
var indices:Vector.<uint> = mesh.geometry.indices;
var vertices:Vector.<Vector3D> = new Vector.<Vector3D>;
var uvs:Vector.<Point> = new Vector.<Point>;
var vNormals:Vector.<Vector3D> = new Vector.<Vector3D>;
var i:int;
for (i = 0; i < positions.length; i += 3) {
vertices.push(new Vector3D(positions[i], positions[i + 1], positions[i + 2]));
}
for (i = 0; i < texcoords.length; i += 2) {
uvs.push(new Point(texcoords[i], texcoords[i + 1]));
}
for (i = 0; i < normals.length; i += 3) {
vNormals.push(new Vector3D(normals[i], normals[i + 1], normals[i + 2]));
}
var tangents:Vector.<Number> = calcTangent(mesh.geometry.indices, vertices, uvs, vNormals);
var geometry:Geometry = new Geometry();
//if (mesh.geometry.hasAttribute(VertexAttributes.TANGENT4) == false) {
var tan:int = VertexAttributes.TANGENT4;
var pos:int = VertexAttributes.POSITION;
var nor:int = VertexAttributes.NORMAL;
var tex:int = VertexAttributes.TEXCOORDS[0];
var attribute:Array = [
pos, pos, pos,
nor, nor, nor,
tan, tan, tan, tan,
tex, tex
]
geometry.addVertexStream(attribute)
//}
geometry.numVertices = mesh.geometry.numVertices
geometry.indices = mesh.geometry.indices;
geometry.setAttributeValues(VertexAttributes.POSITION, positions);
geometry.setAttributeValues(VertexAttributes.NORMAL, normals);
geometry.setAttributeValues(VertexAttributes.TANGENT4, tangents);
geometry.setAttributeValues(VertexAttributes.TEXCOORDS[0], texcoords);
mesh.geometry = geometry;
}
/**
* 複数のMeshを結合し、1つのMeshにします
* @param meshs
* @return
*/
public static function bindMeshs(meshs:Vector.<Mesh>):Mesh {
var count:uint = meshs.length;
var indices:Vector.<uint> = new Vector.<uint>();
var positions:Vector.<Number> = new Vector.<Number>();
var texcoords:Vector.<Number> = new Vector.<Number>();
var nextIndex:uint = 0;
var nextPosition:uint = 0;
var mesh:Mesh = meshs[i];
var i:int
var j:int
for (i = 0; i < count; i++) {
mesh = meshs[i];
var tempPositions:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.POSITION);
mesh.matrix.transformVectors(tempPositions, tempPositions);
positions = positions.concat(tempPositions);
texcoords = texcoords.concat(mesh.geometry.getAttributeValues(VertexAttributes.TEXCOORDS[0]));
var tempIndices:Vector.<uint> = mesh.geometry.indices;
var indexCount:uint = tempIndices.length
for (j = 0; j < indexCount; j++) {
tempIndices[j] += nextIndex;
}
indices = indices.concat(tempIndices);
nextIndex += tempPositions.length/3
}
var geometry:Geometry = new Geometry();
var attributes:Array = [];
attributes[0] = VertexAttributes.POSITION;
attributes[1] = VertexAttributes.POSITION;
attributes[2] = VertexAttributes.POSITION;
attributes[3] = VertexAttributes.TEXCOORDS[0];
attributes[4] = VertexAttributes.TEXCOORDS[0];
geometry.addVertexStream(attributes);
geometry.numVertices = positions.length/3;
geometry.setAttributeValues(VertexAttributes.POSITION, positions);
geometry.setAttributeValues(VertexAttributes.TEXCOORDS[0], texcoords);
geometry.indices = indices;
var result:Mesh = new Mesh()
result.geometry = geometry;
//サーフェースのコピー
var indexBegin:uint = 0
for (i = 0; i < count; i++) {
mesh = meshs[i];
for (j = 0; j < mesh.numSurfaces; j++) {
var surface:Surface = mesh.getSurface(j);
result.addSurface(surface.material, surface.indexBegin+indexBegin, surface.numTriangles)
}
indexBegin = surface.indexBegin+indexBegin + surface.numTriangles * 3;
}
//normal再計算
createNormal(result);
return result;
}
/**
* Cylinder、Cone、Dome、RoundMesh等を合成した、MeshのSurfaceを合成します
* UVのV値のみ更新されます
*
* 頂点情報の高さ(Z座標)で判断します
*
*/
public static function repairRoundSurface(mesh:Mesh):Mesh {
var positions:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.POSITION);
var texcoords:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.TEXCOORDS[0]);
var count:int = positions.length / 3
//全体の高さを割り出す
var minY:Number=0
var maxY:Number=0
for (var i:int = 0; i < count; i++) {
if (minY > positions[i * 3 + 2])
minY = positions[i * 3 + 2];
if (maxY < positions[i * 3 + 2])
maxY = positions[i * 3 + 2];
}
var height:Number = maxY - minY;
for (i = 0; i < count; i++) {
texcoords[i * 2 + 1] = (positions[i * 3 + 2] - minY) / height;
}
mesh.geometry.setAttributeValues(VertexAttributes.TEXCOORDS[0], texcoords);
var result:Mesh = new Mesh();
result.geometry = mesh.geometry
result.addSurface(null, 0, positions.length / 9);
return result;
}
/**
* サーフェースのコピー
* @param origin
* @param mesh
*/
public static function copySurface(origin:Mesh, mesh:Mesh):void {
for (var i:uint = 0; i < origin.numSurfaces; i++) {
var surface:Surface = origin.getSurface(i);
mesh.addSurface(surface.material, surface.indexBegin, surface.numTriangles)
}
}
/**
* TANGENT4を再計算
* @param indices
* @param vertex
* @param uvs
* @param normals
* @return
*/
static public function calcTangent(indices:Vector.<uint>, vertices:Vector.<Vector3D>, uvs:Vector.<Point>, normals:Vector.<Vector3D>):Vector.<Number> {
var tangent:Vector.<Number> = new Vector.<Number>;
var numTriangle:int = indices.length / 3;
var numVertex:int = vertices.length;
var tan1:Vector.<Vector3D> = new Vector.<Vector3D>;
var tan2:Vector.<Vector3D> = new Vector.<Vector3D>;
var i:int;
for (i = 0; i < vertices.length; i++) {
tan1.push(new Vector3D());
tan2.push(new Vector3D());
}
var max:int = indices.length;
for (i = 0; i < max; i += 3) {
var i1:Number = indices[i];
var i2:Number = indices[i + 1];
var i3:Number = indices[i + 2];
var v1:Vector3D = vertices[i1];
var v2:Vector3D = vertices[i2];
var v3:Vector3D = vertices[i3];
var w1:Point = uvs[i1];
var w2:Point = uvs[i2];
var w3:Point = uvs[i3];
var x1:Number = v2.x - v1.x;
var x2:Number = v3.x - v1.x;
var y1:Number = v2.y - v1.y;
var y2:Number = v3.y - v1.y;
var z1:Number = v2.z - v1.z;
var z2:Number = v3.z - v1.z;
var s1:Number = w2.x - w1.x;
var s2:Number = w3.x - w1.x;
var t1:Number = w2.y - w1.y;
var t2:Number = w3.y - w1.y;
var r:Number = 1 / (s1 * t2 - s2 * t1);
var sdir:Vector3D = new Vector3D((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
var tdir:Vector3D = new Vector3D((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
tan1[i1].incrementBy(sdir);
tan1[i2].incrementBy(sdir);
tan1[i3].incrementBy(sdir);
tan2[i1].incrementBy(tdir);
tan2[i2].incrementBy(tdir);
tan2[i3].incrementBy(tdir);
}
for (i = 0; i < numVertex; i++) {
var n:Vector3D = normals[i];
var t:Vector3D = tan1[i];
var tgt:Vector3D = t.subtract(getScaled(n, dot(n, t)));
tgt.normalize();
var w:Number = dot(cross(n, t), tan2[i]) < 0 ? -1 : 1;
tangent.push(tgt.x, tgt.y, tgt.z, w);
}
return tangent;
}
/**
* 2つのベクトルの内積を返します。
* (内積:2つのベクトルがどれだけ平行に近いかを示す数値)
* ・ 1 に近いほど同じ向きで平行
* ・ 0 に近いほど直角
* ・-1 に近いほど逆向きで平行
*/
static public function dot(a:Vector3D, b:Vector3D):Number {
return (a.x * b.x) + (a.y * b.y) + (a.z * b.z);
}
/**
* 2つのベクトルの外積を返します。
* (外積:2つのベクトルで作られる面に垂直なベクトル(=法線)。)
*/
static public function cross(a:Vector3D, b:Vector3D):Vector3D {
return new Vector3D((a.y * b.z) - (a.z * b.y), (a.z * b.x) - (a.x * b.z), (a.x * b.y) - (a.y * b.x));
}
/**
* スケーリングした新しいベクトルを取得
* @param v
* @param scale
* @return
*/
static public function getScaled(v:Vector3D, scale:Number):Vector3D {
var sv:Vector3D = v.clone();
sv.scaleBy(scale);
return sv;
}
/**
* Jointの位置を初期化
* @param joints
*/
public static function JointBindPose(joints:Vector.<Joint>):void {
var count:uint = joints.length;
for (var i:uint = 0; i < count; i++)
{
var joint:Joint = joints[i]
var jointMatrix:Matrix3D = joint.concatenatedMatrix.clone();
jointMatrix.transpose();
var jointBindingTransform:Transform3D = new Transform3D();
jointBindingTransform.initFromVector(jointMatrix.rawData);
jointBindingTransform.invert();
var matrixVector:Vector.<Number> = new Vector.<Number>();
matrixVector.push(jointBindingTransform.a);
matrixVector.push(jointBindingTransform.b);
matrixVector.push(jointBindingTransform.c);
matrixVector.push(jointBindingTransform.d);
matrixVector.push(jointBindingTransform.e);
matrixVector.push(jointBindingTransform.f);
matrixVector.push(jointBindingTransform.g);
matrixVector.push(jointBindingTransform.h);
matrixVector.push(jointBindingTransform.i);
matrixVector.push(jointBindingTransform.j);
matrixVector.push(jointBindingTransform.k);
matrixVector.push(jointBindingTransform.l);
joint.setBindPoseMatrix(matrixVector);
}
}
}
class Mipmaps {
public var mipMap:Vector.<BitmapData>;
private var _texture:BitmapData;
public var numMaps:int = 0;
public var mipmapUVCap:Number = 1;
static private const filter:ConvolutionFilter = new ConvolutionFilter(2, 2, [1, 1, 1, 1], 4, 0, false, true);
static private const matrix:Matrix = new Matrix(0.5, 0, 0, 0.5);
static private const rect:Rectangle = new Rectangle();
static private const point:Point = new Point();
public function Mipmaps(texture:BitmapData = null):void {
if (texture != null) this.texture = texture;
}
public function calculateMipMaps():void {
point.x = 0;
point.y = 0;
mipMap = new Vector.<BitmapData>();
numMaps = 0;
mipMap[numMaps] = _texture;
numMaps++;
if (_texture.width % 2 > 0 || _texture.height % 2 > 0) {
throw new Error("Texture must be base2 size");
}
matrix.identity();
matrix.a = 0.5;
matrix.d = 0.5;
filter.preserveAlpha = !_texture.transparent;
var bmp:BitmapData = (_texture.width*_texture.height > 16777215) ? _texture.clone() : new BitmapData(_texture.width, _texture.height, _texture.transparent);
var current:BitmapData = _texture;
rect.x = 0;
rect.y = 0;
rect.width = _texture.width;
rect.height = _texture.height;
while (rect.width % 2 == 0 && rect.height % 2 == 0) {
bmp.applyFilter(current, rect, point, filter);
// bmp.copyPixels(current, rect, point)
rect.width /= 2;
rect.height /= 2;
current = new BitmapData(rect.width, rect.height, _texture.transparent, 0);
current.draw(bmp, matrix, null, null, null, false);
mipMap[numMaps] = current;
numMaps++;
}
bmp.dispose();
}
public function getMipmapOffsetTable():BitmapData {
if (numMaps == 0) throw new Error("No mipmaps found!");
var tileSizePx:int = _texture.width;
var w:int = isPower2(numMaps) ? numMaps : (1 << ( Math.ceil( Math.log( numMaps + .01) * Math.LOG2E ) ) );
var r:int = 255; // tilesize ratio (1, .5, .25)
var g:int = 0; // u offset as a ratio of tile size
var b:int = 0; // whether there is a base u offset ratio of tilesizePx
var t:int = tileSizePx;
var bmpData:BitmapData = new BitmapData( w, 1, false, RGBToHex(r, g, b) );
mipmapUVCap = numMaps / w;
var offset:int = 0;
for (var i:int = 1; i < numMaps; i++) {
r = int ( mipMap[i].width / tileSizePx * 256 ); // as a ratio of base tile size
t >>= 1;
g = int( offset / tileSizePx * 256 );
offset += t;
b = 255;
//if (i == 2) throw new Error([r, g, b]);
bmpData.setPixel(i, 0, RGBToHex(r, g, b) );
}
while ( i < bmpData.width) {
bmpData.setPixel(i, 0, RGBToHex(r, g, b) );
i++;
}
return bmpData;
}
public function pasteMipmaps(dest:BitmapData, destPt:Point):void {
if (numMaps == 0) throw new Error("No mipmaps found!");
var p:Point = point;
var r:Rectangle = rect;
p.x = destPt.x;
p.y = destPt.y;
r.x = 0;
r.y = 0;
r.width = _texture.width;
r.height = _texture.height;
for (var i:int = 1; i < numMaps; i++) {
var src:BitmapData = mipMap[i];
r.width *= .5;
r.height *= .5;
dest.copyPixels(src, r, p);
p.x += r.width;
}
}
private function RGBToHex(r:int, g:int, b:int):uint
{
var hex:uint = ( r << 16 ) | ( g << 8 ) | b;
return hex;
}
public function isPower2(x:int):Boolean {
return (!((~0) ^ (~x + 1)));
}
public function get texture():BitmapData
{
return _texture;
}
public function set texture(value:BitmapData):void
{
_texture = value;
calculateMipMaps();
}
}
import alternativa.engine3d.materials.ShaderProgram;
import alternativa.engine3d.materials.compiler.Linker;
import flash.display3D.Context3D;
class TileIndexMaterialProgram extends ShaderProgram {
public var aPosition:int = -1;
public var aUV:int = -1;
public var aUV1:int = -1;
public var aNormal:int = -1;
public var aTangent:int = -1;
public var cProjMatrix:int = -1;
public var cCameraPosition:int = -1;
public var cAmbientColor:int = -1;
public var cSurface:int = -1;
public var cThresholdAlpha:int = -1;
public var sDiffuse:int = -1;
public var sOpacity:int = -1;
public var sBump:int = -1;
public var sGlossiness:int = -1;
public var sSpecular:int = -1;
public var sLightMap:int = -1;
public var cPosition:Vector.<int>;
public var cRadius:Vector.<int>;
public var cAxis:Vector.<int>;
public var cColor:Vector.<int>;
public var cF:int = -1;
public var cAtlasTiles:int = -1;
public var cTilePaddSize:int = -1;
public var cTileFrac:int = -1;
public var sTileIndices:int = -1;
public var sMipmapOffsets:int = -1;
public function TileIndexMaterialProgram(vertex:Linker, fragment:Linker, numLigths:int) {
super(vertex, fragment);
cPosition = new Vector.<int>(numLigths);
cRadius = new Vector.<int>(numLigths);
cAxis = new Vector.<int>(numLigths);
cColor = new Vector.<int>(numLigths);
}
override public function upload(context3D:Context3D):void {
super.upload(context3D);
aPosition = vertexShader.findVariable("aPosition");
aUV = vertexShader.findVariable("aUV");
aUV1 = vertexShader.findVariable("aUV1");
aNormal = vertexShader.findVariable("aNormal");
aTangent = vertexShader.findVariable("aTangent");
cProjMatrix = vertexShader.findVariable("cProjMatrix");
cCameraPosition = vertexShader.findVariable("cCameraPosition");
cAmbientColor = fragmentShader.findVariable("cAmbientColor");
cSurface = fragmentShader.findVariable("cSurface");
cThresholdAlpha = fragmentShader.findVariable("cThresholdAlpha");
sDiffuse = fragmentShader.findVariable("sDiffuse");
sOpacity = fragmentShader.findVariable("sOpacity");
sBump = fragmentShader.findVariable("sBump");
sGlossiness = fragmentShader.findVariable("sGlossiness");
sSpecular = fragmentShader.findVariable("sSpecular");
sLightMap = fragmentShader.findVariable("sLightMap");
sTileIndices = fragmentShader.findVariable("sTileIndices");
sMipmapOffsets = fragmentShader.findVariable("sMipmapOffsets");
cTilePaddSize = fragmentShader.findVariable("cTilePaddSize"); // u padding, v padding on atlas. u and v tile sizes + padding on atlas.
cTileFrac = fragmentShader.findVariable("cTileFrac"); // u and v recriprocal fractionals for tile index map, u and v tile sizes on atlas
cF = fragmentShader.findVariable("cF");
cAtlasTiles = fragmentShader.findVariable("cAtlasTiles");
var count:int = cPosition.length;
for (var i:int = 0; i < count; i++) {
cPosition[i] = fragmentShader.findVariable("c" + i + "Position");
cRadius[i] = fragmentShader.findVariable("c" + i + "Radius");
cAxis[i] = fragmentShader.findVariable("c" + i + "Axis");
cColor[i] = fragmentShader.findVariable("c" + i + "Color");
}
}
}
/**
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.DrawUnit;
import alternativa.engine3d.core.Light3D;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Renderer;
import alternativa.engine3d.core.Transform3D;
import alternativa.engine3d.core.VertexAttributes;
import alternativa.engine3d.lights.DirectionalLight;
import alternativa.engine3d.lights.OmniLight;
import alternativa.engine3d.lights.SpotLight;
import alternativa.engine3d.materials.A3DUtils;
import alternativa.engine3d.materials.compiler.Linker;
import alternativa.engine3d.materials.compiler.Procedure;
import alternativa.engine3d.materials.compiler.VariableType;
import alternativa.engine3d.materials.Material;
import alternativa.engine3d.materials.NormalMapSpace;
import alternativa.engine3d.materials.TextureMaterial;
import alternativa.engine3d.objects.Surface;
import alternativa.engine3d.resources.BitmapTextureResource;
import alternativa.engine3d.resources.Geometry;
import alternativa.engine3d.resources.TextureResource;
import avmplus.getQualifiedClassName;
import flash.display3D.Context3D;
import flash.display3D.Context3DBlendFactor;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DVertexBufferFormat;
import flash.display3D.VertexBuffer3D;
import flash.utils.Dictionary;
import flash.utils.getDefinitionByName;
use namespace alternativa3d;
/**
* This modified StandardMaterial fills a surface with varying square bitmap image tiles by looking up a texture from a regular tile sprite-sheet atlas. Selection of tile is based on a set of uploaded
* bitmaps containing the information on where to locate the tile in the atlas. The material also intends to support orthogonal rotation of tiles (ie, rotation tranform index), using the RGB channels respectively).
* This material is mainly used for opaque tile-based terrain and floors with a variety of pre-built patterns.
*
* @author Glenn
*/
class TileIndexMaterial extends TextureMaterial {
private static const LIGHT_MAP_BIT:int = 1;
private static const GLOSSINESS_MAP_BIT:int = 2;
private static const SPECULAR_MAP_BIT:int = 4;
private static const OPACITY_MAP_BIT:int = 8;
private static const NORMAL_MAP_SPACE_OFFSET:int = 4; // shift value
private static const ALPHA_TEST_OFFSET:int = 6;
private static const OMNI_LIGHT_OFFSET:int = 8;
private static const DIRECTIONAL_LIGHT_OFFSET:int = 11;
private static const SPOT_LIGHT_OFFSET:int = 14;
private static const SHADOW_OFFSET:int = 17;
// TODO: remove double cash by transform procedure. It increase speed by 1%
// private static const OBJECT_TYPE_BIT:int = 19;
private static var caches:Dictionary = new Dictionary(true);
private var cachedContext3D:Context3D;
private var programsCache:Dictionary;
private var groups:Vector.<Vector.<Light3D>> = new Vector.<Vector.<Light3D>>();
/**
* @private
* Procedure for diffuse map texture atlas with alpha channel and custom per-pixel mipmapping uv offsets
*/
///*
static alternativa3d const getDiffuseProcedure:Procedure = new Procedure([
"#v0=vUV",
"#v1=vZDistance",
"#s0=sDiffuse",
"#s1=sTileIndices", // sample texture index for tile indices
"#s2=sMipmapOffsets",
"#c0=cThresholdAlpha",
"#c1=cTilePaddSize",
"#c2=cTileFrac", // 1/ noTilesAcross u and v directions, + tileSizeWidth and tileSizeHeight in UV coordinates within atlas
"#c3=cF", //(f=camera.focalLength / resolution, totalMipmaps, mipmapUVCap, 1)
"#c4=cAtlasTiles",
"mov t1, v1",
"div t1.x, t1.z, c3.x", // Calculate mipmap level manually per pixel (z >= f) ? (log(z/f)) : 0;
"log t1.x, t1.x", // binary log
"frc t1.y, t1.x", // round down
"sub t1.x, t1.x, t1.y",
"div t1.x, t1.x, c3.y", // get uv sample ratio of current mipmap level against total mipmaps
"mul t1.x, t1.x, c3.z", // clamp ratio to base uv cap amount
"tex t2, t1, s2 <2d,nearest,clamp,nomip>", // sample mipmap level offset lookup
"mul t2.y, t2.y, c2.z", // as actual u offset value
"mul t2.w, t2.z, c2.z", // this value is either zero or 1*tileSize, depending on t2.z is either zero or 1.
"add t2.y, t2.y, t2.w", // last operand should be t2.w
//"mov t1, c1", // dummy usage if needed
"tex t1, v0, s1 <2d,nearest,clamp,nomip>", // sample t1 tile index lookup
"div t1.x, t1.x, t1.w",
// "mov t1.w, c3.w",
// Binary subdivision
"mul t0.x, t1.x, c4.z", // let's half-split R channel's 8 bits to 4 by 4 bits.
"frc t0.y, t0.x", // lower 4 bits fractional
"sub t0.x, t0.x, t1.y", // get floored value by minusing off floating point value by lower 4 bits
"mul t1.x, t0.x, c4.w", // upper 4 bits whole determined by
"mul t1.x, t1.x, c4.x", // REPEAT SPLIT again: now need to get upper 2 bits from the above upper 4 bits
"frc t0.y, t1.x", // this value contains matrix.a fractional
"sub t1.x, t1.x, t0.y",
"mul t1.x, t1.x, c4.y", // finalise 2 upper bits which is the U and V doordinate for texture 1 (Red) accordingly.
"mov t1.y, t0.y",
"sub t1.xy, t1.xy, c1.xy",
"add t1.x, t1.x, c1.x", // add a half pixel forward padding to existing lookup tile index lookup, only if mipmapping is used
"add t1.x, t1.x, c1.x",
"add t1.y, t1.y, c1.y", // add a half pixel forward padding to existing lookup tile index lookup, only if min is found
"add t1.y, t1.y, c1.y",
"mov t0, v0", // dummy usage required somehow..
//"mov ft3, v0",
// ------ get normalized tile-based uv offsets with fractionals
"div t0.xy, t0.xy, c2.xy",
"frc t0.xy, t0.xy",
// todo: also rotate t0 normalized coordinates according to t1.z rotation index.
// TEXTURE ATLAS CODES (if no texture atlas is used, constants would be 0s or 1s preventing any effect to destination variables)
// "mul t0.xy, t0.xy, t2.xx", // take into consideration tile size reduction ratio due to mipmapping
"mul t2.z, c2.z, t2.x",
"mul t2.w, c2.w, t2.x",
"sub t2.z, t2.z, c1.x", // manual clamping tweaks to be composed to constant!
"sub t2.z, t2.z, c1.x",
"sub t2.z, t2.z, c1.x",
"sub t2.w, t2.w, c1.y",
"sub t2.w, t2.w, c1.y",
"sub t2.w, t2.w, c1.y",
"sub t2.w, t2.w, c1.y",
"sub t2.w, t2.w, c1.y",
"sub t2.w, t2.w, c1.y",
"sub t2.w, t2.w, c1.y",
"sub t2.w, t2.w, c1.y",
// "mul t0.xy, t0.xy, t2.zw", // normalized tile-based uv offset * tile uv size on atlas .
"mul t0.x, t0.x, t2.z", // normalized tile-based uv offset * tile uv size on atlas .
"mul t0.y, t0.y, t2.w", // normalized tile-based uv offset * tile uv size on atlas .
"add t0.xy, t0.xy, t1.xy", // finally, add the tile origin atlas uv to the current tile uv offset
// Get final clamped sample
"add t0.x, t0.x, t2.y", // add mipmap u offset
"tex t0, t0, s0 <2d, linear,clamp, nomip>",
"mul t0.w, t0.w, c0.w",
"mov o0, t0"
], "getDiffuseProcedure");
//*/
/**
* @private
* Procedure for diffuse with opacity map.
*/
static alternativa3d const getDiffuseOpacityProcedure:Procedure = new Procedure([ // TODO: (if needed). Most cases i'm dealing with fully opaque textures only.
"#v0=vUV",
"#s0=sDiffuse",
"#s1=sOpacity",
"#c0=cThresholdAlpha",
"tex t0, v0, s0 <2d, linear,clamp, miplinear>",
"tex t1, v0, s1 <2d, linear,repeat, miplinear>",
"mul t0.w, t1.x, c0.w",
"mov o0, t0"
], "getDiffuseOpacityProcedure");
/**
* @private
*/
alternativa3d static const DISABLED:int = 0;
/**
* @private
*/
alternativa3d static const SIMPLE:int = 1;
/**
* @private
*/
alternativa3d static const ADVANCED:int = 2;
/**
* @private
*/
alternativa3d static var fogMode:int = DISABLED;
/**
* @private
*/
alternativa3d static var fogNear:Number = 1000;
/**
* @private
*/
alternativa3d static var fogFar:Number = 5000;
/**
* @private
*/
alternativa3d static var fogMaxDensity:Number = 1;
/**
* @private
*/
alternativa3d static var fogColorR:Number = 0xC8/255;
/**
* @private
*/
alternativa3d static var fogColorG:Number = 0xA2/255;
/**
* @private
*/
alternativa3d static var fogColorB:Number = 0xC8/255;
/**
* @private
*/
alternativa3d static var fogTexture:TextureResource;
// inputs : position
private static const _passVaryingsProcedure:Procedure = new Procedure([
"#v0=vPosition",
"#v1=vViewVector",
"#c0=cCameraPosition",
// Pass the position
"mov v0, i0",
// Vector to Camera
"sub t0, c0, i0",
"mov v1.xyz, t0.xyz",
"mov v1.w, c0.w"
]);
// inputs : tangent, normal
private static const _passTBNRightProcedure:Procedure = getPassTBNProcedure(true);
private static const _passTBNLeftProcedure:Procedure = getPassTBNProcedure(false);
private static function getPassTBNProcedure(right:Boolean):Procedure {
var crsInSpace:String = (right) ? "crs t1.xyz, i0, i1" : "crs t1.xyz, i1, i0";
return new Procedure([
"#v0=vTangent",
"#v1=vBinormal",
"#v2=vNormal",
// Calculate binormal
crsInSpace,
"mul t1.xyz, t1.xyz, i0.w",
// Транспонируем матрицу нормалей
"mov v0.xyzw, i1.xyxw",
"mov v0.x, i0.x",
"mov v0.y, t1.x",
"mov v1.xyzw, i1.xyyw",
"mov v1.x, i0.y",
"mov v1.y, t1.y",
"mov v2.xyzw, i1.xyzw",
"mov v2.x, i0.z",
"mov v2.y, t1.z"
], "passTBNProcedure");
}
// outputs : light, highlight
private static const _ambientLightProcedure:Procedure = new Procedure([
"#c0=cSurface",
"mov o0, i0",
"mov o1, c0.xxxx"
], "ambientLightProcedure");
// Set o.w to glossiness
private static const _setGlossinessFromConstantProcedure:Procedure = new Procedure([
"#c0=cSurface",
"mov o0.w, c0.y"
], "setGlossinessFromConstantProcedure");
// Set o.w to glossiness from texture
private static const _setGlossinessFromTextureProcedure:Procedure = new Procedure([
"#v0=vUV",
"#c0=cSurface",
"#s0=sGlossiness",
"tex t0, v0, s0 <2d, repeat, linear, miplinear>",
"mul o0.w, t0.x, c0.y"
], "setGlossinessFromTextureProcedure");
// outputs : normal, viewVector
private static const _getNormalAndViewTangentProcedure:Procedure = new Procedure([
"#v0=vTangent",
"#v1=vBinormal",
"#v2=vNormal",
"#v3=vUV",
"#v4=vViewVector",
"#c0=cAmbientColor",
"#s0=sBump",
// Extract normal from the texture
"tex t0, v3, s0 <2d,repeat,linear,miplinear>",
"add t0, t0, t0",
"sub t0.xyz, t0.xyz, c0.www",
// Transform the normal with TBN
"nrm t1.xyz, v0.xyz",
"dp3 o0.x, t0.xyz, t1.xyz",
"nrm t1.xyz, v1.xyz",
"dp3 o0.y, t0.xyz, t1.xyz",
"nrm t1.xyz, v2.xyz",
"dp3 o0.z, t0.xyz, t1.xyz",
// Normalization
"nrm o0.xyz, o0.xyz",
// Returns normalized vector of view
"nrm o1.xyz, v4"
], "getNormalAndViewTangentProcedure");
// outputs : normal, viewVector
private static const _getNormalAndViewObjectProcedure:Procedure = new Procedure([
"#v3=vUV",
"#v4=vViewVector",
"#c0=cAmbientColor",
"#s0=sBump",
// Extract normal from the texture
"tex t0, v3, s0 <2d,repeat,linear,miplinear>",
"add t0, t0, t0",
"sub t0.xyz, t0.xyz, c0.www",
// Normalization
"nrm o0.xyz, t0.xyz",
// Returns normalized vector of view
"nrm o1.xyz, v4"
], "getNormalAndViewObjectProcedure");
// Apply specular map color to a flare
private static const _applySpecularProcedure:Procedure = new Procedure([
"#v0=vUV",
"#s0=sSpecular",
"tex t0, v0, s0 <2d, repeat,linear,miplinear>",
"mul o0.xyz, o0.xyz, t0.xyz"
], "applySpecularProcedure");
//Apply light and flare to diffuse
// inputs : "diffuse", "tTotalLight", "tTotalHighLight"
private static const _mulLightingProcedure:Procedure = new Procedure([
"#c0=cSurface", // c0.z - specularPower
"mul i0.xyz, i0.xyz, i1.xyz",
"mul t1.xyz, i2.xyz, c0.z",
"add i0.xyz, i0.xyz, t1.xyz",
"mov o0, i0"
], "mulLightingProcedure");
private static const passZDistanceProcedure:Procedure = new Procedure([
"#v1=vZDistance",
"#c0=cZSpace",
"dp4 t0.z, i0, c0",
"mov v1, t0.zzzz"
], "passZDistanceProcedure" );
// inputs : position
private static const passSimpleFogConstProcedure:Procedure = new Procedure([
"#v0=vZDistance",
"#c0=cFogSpace",
"dp4 t0.z, i0, c0",
"mov v0, t0.zzzz",
"sub v0.y, i0.w, t0.z"
], "passSimpleFogConst");
// inputs : color
private static const outputWithSimpleFogProcedure:Procedure = new Procedure([
"#v0=vZDistance",
"#c0=cFogColor",
"#c1=cFogRange",
// Restrict fog factor with the range
"min t0.xy, v0.xy, c1.xy",
"max t0.xy, t0.xy, c1.zw",
"mul i0.xyz, i0.xyz, t0.y",
"mul t0.xyz, c0.xyz, t0.x",
"add i0.xyz, i0.xyz, t0.xyz",
"mov o0, i0"
], "outputWithSimpleFog");
// inputs : position, projected
private static const postPassAdvancedFogConstProcedure:Procedure = new Procedure([
"#v0=vZDistance",
"#c0=cFogSpace",
"dp4 t0.z, i0, c0",
"mov v0, t0.zzzz",
"sub v0.y, i0.w, t0.z",
// Screen x coordinate
"mov v0.zw, i1.xwxw",
"mov o0, i1"
], "postPassAdvancedFogConst");
// inputs : color
private static const outputWithAdvancedFogProcedure:Procedure = new Procedure([
"#v0=vZDistance",
"#c0=cFogConsts",
"#c1=cFogRange",
"#s0=sFogTexture",
// Restrict fog factor with the range
"min t0.xy, v0.xy, c1.xy",
"max t0.xy, t0.xy, c1.zw",
"mul i0.xyz, i0.xyz, t0.y",
// Calculate fog color
"mov t1.xyzw, c0.yyzw",
"div t0.z, v0.z, v0.w",
"mul t0.z, t0.z, c0.x",
"add t1.x, t1.x, t0.z",
"tex t1, t1, s0 <2d, repeat, linear, miplinear>",
"mul t0.xyz, t1.xyz, t0.x",
"add i0.xyz, i0.xyz, t0.xyz",
"mov o0, i0"
], "outputWithAdvancedFog");
// Add lightmap value with light
private static const _addLightMapProcedure:Procedure = new Procedure([
"#v0=vUV1",
"#s0=sLightMap",
"tex t0, v0, s0 <2d,repeat,linear,miplinear>",
"add t0, t0, t0",
"add o0.xyz, i0.xyz, t0.xyz"
], "applyLightMapProcedure");
private static const _passLightMapUVProcedure:Procedure = new Procedure([
"#a0=aUV1",
"#v0=vUV1",
"mov v0, a0"
], "passLightMapUVProcedure");
/**
* Normal map.
*/
public var normalMap:TextureResource;
private var _normalMapSpace:int = NormalMapSpace.TANGENT_RIGHT_HANDED;
/**
* Type of the normal map. Should be defined by constants of <code>NormalMapSpace</code> class.
*
* @default NormalMapSpace.TANGENT
*
* @see NormalMapSpace
*/
public function get normalMapSpace():int {
return _normalMapSpace;
}
/**
* @private
*/
public function set normalMapSpace(value:int):void {
if (value != NormalMapSpace.TANGENT_RIGHT_HANDED && value != NormalMapSpace.TANGENT_LEFT_HANDED && value != NormalMapSpace.OBJECT) {
throw new ArgumentError("Value must be a constant from the NormalMapSpace class");
}
_normalMapSpace = value;
}
/**
* Specular map.
*/
public var specularMap:TextureResource;
/**
* Glossiness map.
*/
public var glossinessMap:TextureResource;
/**
* Light map.
*/
public var lightMap:TextureResource;
/**
* Number of the UV-channel for light map.
*/
public var lightMapChannel:uint = 0;
/**
* Glossiness. Multiplies with <code>glossinessMap</code> value.
*/
public var glossiness:Number = 100;
/**
* Brightness of a flare. Multiplies with <code>specularMap</code> value.
*/
public var specularPower:Number = 1;
/**
* Creates a new TileIndexMaterial instance.
* @param diffuseMap Diffuse map.
* @param normalMap Normal map.
* @param specularMap Specular map.
* @param glossinessMap Glossiness map.
* @param opacityMap Opacity map.
*/
// texture atlas values
private var atlasSheet:BitmapTextureResource;
private var tilePaddingPxH:int;
private var tilePaddingPxV:int;
private var tileSizePx:int;
private var tilePaddingU:Number;
private var tilePaddingV:Number;
private var tileSizeU:Number;
private var tileSizeV:Number;
public var resolution:Number = 1;
private var tileIndexMaps:Vector.<BitmapTextureResource>; // lookup tile indices. Can store multiple lookups for this material, allowing multiple surfaces to use same material.
private var normalMaps:Vector.<BitmapTextureResource>;
private var lightMaps:Vector.<BitmapTextureResource>;
public var lookupIndex:int = 0; // Set this variable directly prior to rendering through this material to change lookup tile index table.
private var totalMipmaps:int;
private var mipmapTable:BitmapTextureResource;
private var mipmapUVCap:Number;
// for rotation of tiles (todo: later..)
private var uvTransformRef:BitmapTextureResource; // lookup table of uv rotation values for orthogonal rotations.
public function TileIndexMaterial(atlasSheet:BitmapTextureResource, mipmapTable:BitmapTextureResource, mipmapUVCap:Number, tileIndexMaps:Vector.<BitmapTextureResource>, normalMap:TextureResource, lightMaps:Vector.<BitmapTextureResource>, uvTransformRef:BitmapTextureResource, tileSizePx:int, tilePaddingPxH:int=0, tilePaddingPxV:int=-1) {
this.tileIndexMaps = tileIndexMaps;
this.lightMaps = lightMaps;
this.mipmapUVCap = mipmapUVCap;
this.mipmapTable = mipmapTable;
this.totalMipmaps = mipmapUVCap * mipmapTable.data.width;
if (tilePaddingPxV < 0) tilePaddingPxV = tilePaddingPxH;
//diffuseMap:TextureResource = null, normalMap:TextureResource = null, specularMap:TextureResource = null, glossinessMap:TextureResource = null, opacityMap:TextureResource = null
super(atlasSheet, null, 1); // no opacity map suupport
this.atlasSheet = atlasSheet;
this.normalMap = normalMap;
this.specularMap = specularMap;
this.glossinessMap = glossinessMap;
this.uvTransformRef = uvTransformRef;
this.tileIndexMaps = tileIndexMaps;
tilePaddingU = .5 / atlasSheet.data.width; // actually padding should be .5/dimension for mipmap level zero.
tilePaddingV = .5/ atlasSheet.data.height;
// todo: clamping to half of pixel uv to
tileSizeU = tileSizePx / atlasSheet.data.width;// - .5 * (1/atlasSheet.data.width);
tileSizeV = tileSizePx / atlasSheet.data.height;//- .5 * (1/atlasSheet.data.height);
this.tilePaddingPxH = tilePaddingPxH;
this.tilePaddingPxV = tilePaddingPxV;
this.tileSizePx = tileSizePx;
}
/**
* @private
*/
override alternativa3d function fillResources(resources:Dictionary, resourceType:Class):void {
super.fillResources(resources, resourceType);
if (normalMap != null &&
A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(normalMap)) as Class, resourceType)) {
resources[normalMap] = true;
}
if (mipmapTable != null &&
A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(mipmapTable)) as Class, resourceType)) {
resources[mipmapTable] = true;
}
/*
if (lightMap != null &&
A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(lightMap)) as Class, resourceType)) {
resources[lightMap] = true;
}
if (glossinessMap != null &&
A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(glossinessMap)) as Class, resourceType)) {
resources[glossinessMap] = true;
}
if (specularMap != null &&
A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(specularMap)) as Class, resourceType)) {
resources[specularMap] = true;
}
*/
var len:int;
var i:int;
var res:TextureResource;
len = tileIndexMaps.length;
for (i=0; i < len; i++) {
res = tileIndexMaps[i];
if ( A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(res)) as Class, resourceType)) resources[res] = true;
}
len = lightMaps.length;
for (i = 0; i < len; i++) {
res = lightMaps[i];
if (res == null) continue;
if ( A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(res)) as Class, resourceType)) resources[res] = true;
}
}
/**
* @private
*/
alternativa3d function setPassUVProcedureConstants(destination:DrawUnit, vertexLinker:Linker):void {
}
// inputs: tNormal", "tViewVector", "shadow", "cAmbientColor"
// outputs : light, hightlight
private function formDirectionalProcedure(procedure:Procedure, index:int, useShadow:Boolean):void {
var source:Array = [
// Position - dirction vector of light
"#c0=c" + index + "Position",
"#c1=c" + index + "Color",
// Calculate half-way vector
"add t0.xyz, i1.xyz, c0.xyz",
"mov t0.w, c0.w",
"nrm t0.xyz,t0.xyz",
// Calculate a flare
"dp3 t0.w, t0.xyz, i0.xyz",
"pow t0.w, t0.w, o1.w",
// Calculate light
"dp3 t0.x, i0.xyz, c0.xyz",
"sat t0.x, t0.x"
];
if (useShadow) {
source.push("mul t0.xw, t0.xw, i2.x");
source.push("mul t0.xyz, c1.xyz, t0.xxx");
source.push("add o0.xyz, t0.xyz, i3.xyz");
source.push("mul o1.xyz, c1.xyz, t0.www");
} else {
// Apply calculated values
source.push("mul t0.xyz, c1.xyz, t0.xxxx");
source.push("add o0, o0, t0.xyz");
source.push("mul t0.xyz, c1.xyz, t0.w");
source.push("add o1.xyz, o1.xyz, t0.xyz");
}
procedure.compileFromArray(source);
}
private function formOmniProcedure(procedure:Procedure, index:int, useShadow:Boolean):void {
// fragmentLinker.setInputParams(omniMulShadowProcedure, "tNormal", "tViewVector", "tTotalLight", "cAmbientColor");
var source:Array = [
"#c0=c" + index + "Position",
"#c1=c" + index + "Color",
"#c2=c" + index + "Radius",
"#v0=vPosition"
];
if (useShadow) {
// Считаем вектор из точки к свету
source.push("sub t0, c0, v0"); // L = lightPos - PointPos
source.push("dp3 t0.w, t0.xyz, t0.xyz"); // lenSqr
source.push("nrm t0.xyz, t0.xyz"); // L = normalize(L)
// Считаем half-way вектор
source.push("add t1.xyz, i1.xyz, t0.xyz");
source.push("nrm t1.xyz, t1.xyz");
// Считаем блик
source.push("dp3 t1.w, t1.xyz, i0.xyz");
source.push("pow t1.w, t1.w, o1.w");
// Считаем расстояние до источника света
source.push("sqt t1.x, t0.w"); // len = sqt(lensqr)
// Считаем свет
source.push("dp3 t0.w, t0.xyz, i0.xyz"); // dot = dot(normal, L)
// Считаем затухание
source.push("sub t0.x, t1.x, c2.z"); // len = len - atenuationBegin
source.push("div t0.y, t0.x, c2.y"); // att = len/radius
source.push("sub t0.x, c2.x, t0.y"); // att = 1 - len/radius
source.push("sat t0.xw, t0.xw"); // t = max(t, 0)
// i3 - ambient
// i2 - shadow-test
source.push("mul t0.xw, t0.xwww, i2.xxxx");
source.push("mul t0.xyz, c1.xyz, t0.xxx"); // t = color*t
source.push("mul t1.xyz, t0.xyz, t1.w");
source.push("add o1.xyz, o1.xyz, t1.xyz");
source.push("mul t0.xyz, t0.xyz, t0.www");
source.push("add o0.xyz, t0.xyz, i3.xyz");
} else {
// Считаем вектор из точки к свету
source.push("sub t0, c0, v0"); // L = lightPos - PointPos
source.push("dp3 t0.w, t0.xyz, t0.xyz"); // lenSqr
source.push("nrm t0.xyz, t0.xyz"); // L = normalize(L)
// Считаем half-way вектор
source.push("add t1.xyz, i1.xyz, t0.xyz");
source.push("mov t1.w, c0.w");
source.push("nrm t1.xyz, t1.xyz");
// Считаем блик
source.push("dp3 t1.w, t1.xyz, i0.xyz");
source.push("pow t1.w, t1.w, o1.w"); //!!!
// Считаем расстояние до источника света
source.push("sqt t1.x, t0.w"); // len = sqt(lensqr)
// Считаем свет
source.push("dp3 t0.w, t0.xyz, i0.xyz"); // dot = dot(normal, L)
// Считаем затухание
source.push("sub t0.x, t1.x, c2.z"); // len = len - atenuationBegin
source.push("div t0.y, t0.x, c2.y"); // att = len/radius
source.push("sub t0.x, c2.x, t0.y"); // att = 1 - len/radius
source.push("sat t0.xw, t0.xw"); // t = max(t, 0)
// Перемножаем цвет источника с затуханием
source.push("mul t0.xyz, c1.xyz, t0.xxx"); // t = color*t
source.push("mul t1.xyz, t0.xyz, t1.w");
source.push("add o1.xyz, o1.xyz, t1.xyz");
source.push("mul t0.xyz, t0.xyz, t0.www");
source.push("add o0.xyz, o0.xyz, t0.xyz");
}
procedure.compileFromArray(source);
}
/**
* @param object
* @param materialKey
* @param opacityMap
* @param alphaTest 0:disabled 1:alpha-test 2:contours
* @param lightsGroup
* @param directionalLight
* @param lightsLength
*/
private function getProgram(object:Object3D, programs:Array, camera:Camera3D, materialKey:int, opacityMap:TextureResource, alphaTest:int, lightsGroup:Vector.<Light3D>, lightsLength:int, isFirstGroup:Boolean, shadowedLight:Light3D):TileIndexMaterialProgram {
// 0 bit - lightmap
// 1 bit - glossiness map
// 2 bit - opacity map
// 3 bit - specular map
// 4-5 bits - normalMapSpace
// 6-7 bits - alphaTest
// 8-10 bits - OmniLight count
// 11-13 bits - DirectionalLight count
// 14-16 bits - SpotLight count
// 17-18 bit - Shadow Type (PCF, SIMPLE, NONE)
var key:int = materialKey | (opacityMap != null ? OPACITY_MAP_BIT : 0) | (alphaTest << ALPHA_TEST_OFFSET);
var program:TileIndexMaterialProgram = programs[key];
if (program == null) {
var vertexLinker:Linker = new Linker(Context3DProgramType.VERTEX);
var fragmentLinker:Linker = new Linker(Context3DProgramType.FRAGMENT);
var i:int;
// Merge program using lightsGroup
// add property useShadow
fragmentLinker.declareVariable("tTotalLight");
fragmentLinker.declareVariable("tTotalHighLight");
fragmentLinker.declareVariable("tNormal");
if (isFirstGroup){
fragmentLinker.declareVariable("cAmbientColor", VariableType.CONSTANT);
fragmentLinker.addProcedure(_ambientLightProcedure);
fragmentLinker.setInputParams(_ambientLightProcedure, "cAmbientColor");
fragmentLinker.setOutputParams(_ambientLightProcedure, "tTotalLight", "tTotalHighLight");
if (lightMap != null) {
vertexLinker.addProcedure(_passLightMapUVProcedure);
fragmentLinker.addProcedure(_addLightMapProcedure);
fragmentLinker.setInputParams(_addLightMapProcedure, "tTotalLight");
fragmentLinker.setOutputParams(_addLightMapProcedure, "tTotalLight");
}
}
else{
// сбросить tTotalLight tTotalHighLight
fragmentLinker.declareVariable("cAmbientColor", VariableType.CONSTANT);
fragmentLinker.addProcedure(_ambientLightProcedure);
fragmentLinker.setInputParams(_ambientLightProcedure, "cAmbientColor");
fragmentLinker.setOutputParams(_ambientLightProcedure, "tTotalLight", "tTotalHighLight");
}
var positionVar:String = "aPosition";
var normalVar:String = "aNormal";
var tangentVar:String = "aTangent";
vertexLinker.declareVariable(positionVar, VariableType.ATTRIBUTE);
vertexLinker.declareVariable(tangentVar, VariableType.ATTRIBUTE);
vertexLinker.declareVariable(normalVar, VariableType.ATTRIBUTE);
if (object.transformProcedure != null) {
positionVar = appendPositionTransformProcedure(object.transformProcedure, vertexLinker);
}
vertexLinker.addProcedure(_projectProcedure);
vertexLinker.setInputParams(_projectProcedure, positionVar);
vertexLinker.addProcedure(_passUVProcedure);
// add z distance procedure
vertexLinker.addProcedure(passZDistanceProcedure);
vertexLinker.setInputParams(passZDistanceProcedure, positionVar);
if (glossinessMap != null) {
fragmentLinker.addProcedure(_setGlossinessFromTextureProcedure);
fragmentLinker.setOutputParams(_setGlossinessFromTextureProcedure, "tTotalHighLight");
} else {
fragmentLinker.addProcedure(_setGlossinessFromConstantProcedure);
fragmentLinker.setOutputParams(_setGlossinessFromConstantProcedure, "tTotalHighLight");
}
if (lightsLength > 0 || shadowedLight) {
var procedure:Procedure;
if (object.deltaTransformProcedure != null) {
vertexLinker.declareVariable("tTransformedNormal");
procedure = object.deltaTransformProcedure.newInstance();
vertexLinker.addProcedure(procedure);
vertexLinker.setInputParams(procedure, normalVar);
vertexLinker.setOutputParams(procedure, "tTransformedNormal");
normalVar = "tTransformedNormal";
vertexLinker.declareVariable("tTransformedTangent");
procedure = object.deltaTransformProcedure.newInstance();
vertexLinker.addProcedure(procedure);
vertexLinker.setInputParams(procedure, tangentVar);
vertexLinker.setOutputParams(procedure, "tTransformedTangent");
tangentVar = "tTransformedTangent";
}
vertexLinker.addProcedure(_passVaryingsProcedure);
vertexLinker.setInputParams(_passVaryingsProcedure, positionVar);
fragmentLinker.declareVariable("tViewVector");
if (_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED) {
var nrmProcedure:Procedure = (_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED) ? _passTBNRightProcedure : _passTBNLeftProcedure;
vertexLinker.addProcedure(nrmProcedure);
vertexLinker.setInputParams(nrmProcedure, tangentVar, normalVar);
fragmentLinker.addProcedure(_getNormalAndViewTangentProcedure);
fragmentLinker.setOutputParams(_getNormalAndViewTangentProcedure, "tNormal", "tViewVector");
} else {
fragmentLinker.addProcedure(_getNormalAndViewObjectProcedure);
fragmentLinker.setOutputParams(_getNormalAndViewObjectProcedure, "tNormal", "tViewVector");
}
if (shadowedLight != null) {
var shadowProc:Procedure;
if (shadowedLight is DirectionalLight){
vertexLinker.addProcedure(shadowedLight.shadow.vertexShadowProcedure, positionVar);
shadowProc = shadowedLight.shadow.fragmentShadowProcedure;
fragmentLinker.addProcedure(shadowProc);
fragmentLinker.setOutputParams(shadowProc, "tTotalLight");
var dirMulShadowProcedure:Procedure = new Procedure(null, "lightShadowDirectional");
formDirectionalProcedure(dirMulShadowProcedure, 0, true);
fragmentLinker.addProcedure(dirMulShadowProcedure);
fragmentLinker.setInputParams(dirMulShadowProcedure, "tNormal", "tViewVector", "tTotalLight", "cAmbientColor");
fragmentLinker.setOutputParams(dirMulShadowProcedure, "tTotalLight", "tTotalHighLight");
}
if (shadowedLight is OmniLight){
vertexLinker.addProcedure(shadowedLight.shadow.vertexShadowProcedure, positionVar);
shadowProc = shadowedLight.shadow.fragmentShadowProcedure;
fragmentLinker.addProcedure(shadowProc);
fragmentLinker.setOutputParams(shadowProc, "tTotalLight");
var omniMulShadowProcedure:Procedure = new Procedure(null, "lightShadowDirectional");
formOmniProcedure(omniMulShadowProcedure, 0, true);
fragmentLinker.addProcedure(omniMulShadowProcedure);
fragmentLinker.setInputParams(omniMulShadowProcedure, "tNormal", "tViewVector", "tTotalLight", "cAmbientColor");
fragmentLinker.setOutputParams(omniMulShadowProcedure, "tTotalLight", "tTotalHighLight");
}
}
for (i = 0; i < lightsLength; i++) {
var light:Light3D = lightsGroup[i];
if (light == shadowedLight && (shadowedLight is DirectionalLight || shadowedLight is OmniLight)) continue;
var lightFragmentProcedure:Procedure = new Procedure();
lightFragmentProcedure.name = "light" + i.toString();
if (light is DirectionalLight) {
formDirectionalProcedure(lightFragmentProcedure, i, false);
lightFragmentProcedure.name += "Directional";
} else if (light is OmniLight) {
formOmniProcedure(lightFragmentProcedure, i, false);
lightFragmentProcedure.name += "Omni";
} else if (light is SpotLight) {
lightFragmentProcedure.compileFromArray([
"#c0=c" + i + "Position",
"#c1=c" + i + "Color",
"#c2=c" + i + "Radius",
"#c3=c" + i + "Axis",
"#v0=vPosition",
// Calculate vector from the point to light
"sub t0, c0, v0",// L = pos - lightPos
"dp3 t0.w, t0, t0",// lenSqr
"nrm t0.xyz,t0.xyz",// L = normalize(L)
// Calculate half-way vector
"add t2.xyz, i1.xyz, t0.xyz",
"nrm t2.xyz, t2.xyz",
//Calculate a flare
"dp3 t2.x, t2.xyz, i0.xyz",
"pow t2.x, t2.x, o1.w",
"dp3 t1.x, t0.xyz, c3.xyz", //axisDirDot
"dp3 t0.x, t0, i0.xyz",// dot = dot(normal, L)
"sqt t0.w, t0.w",// len = sqt(lensqr)
"sub t0.w, t0.w, c2.y",// len = len - atenuationBegin
"div t0.y, t0.w, c2.x",// att = len/radius
"sub t0.w, c0.w, t0.y",// att = 1 - len/radius
"sub t0.y, t1.x, c2.w",
"div t0.y, t0.y, c2.z",
"sat t0.xyw,t0.xyw",// t = sat(t)
"mul t1.xyz,c1.xyz,t0.yyy",// t = color*t
"mul t1.xyz,t1.xyz,t0.www",//
"mul t2.xyz, t2.x, t1.xyz",
"add o1.xyz, o1.xyz, t2.xyz",
"mul t1.xyz, t1.xyz, t0.xxx",
"add o0.xyz, o0.xyz, t1.xyz"
]);
lightFragmentProcedure.name += "Spot";
}
fragmentLinker.addProcedure(lightFragmentProcedure);
fragmentLinker.setInputParams(lightFragmentProcedure, "tNormal", "tViewVector");
fragmentLinker.setOutputParams(lightFragmentProcedure, "tTotalLight", "tTotalHighLight");
}
}
var outputProcedure:Procedure;
if (specularMap != null) {
fragmentLinker.addProcedure(_applySpecularProcedure);
fragmentLinker.setOutputParams(_applySpecularProcedure, "tTotalHighLight");
outputProcedure = _applySpecularProcedure;
}
fragmentLinker.declareVariable("tColor");
outputProcedure = opacityMap != null ? getDiffuseOpacityProcedure : getDiffuseProcedure;
fragmentLinker.addProcedure(outputProcedure);
fragmentLinker.setOutputParams(outputProcedure, "tColor");
if (alphaTest > 0) {
outputProcedure = alphaTest == 1 ? thresholdOpaqueAlphaProcedure : thresholdTransparentAlphaProcedure;
fragmentLinker.addProcedure(outputProcedure, "tColor");
fragmentLinker.setOutputParams(outputProcedure, "tColor");
}
fragmentLinker.addProcedure(_mulLightingProcedure, "tColor", "tTotalLight", "tTotalHighLight");
// if (fogMode == SIMPLE || fogMode == ADVANCED) {
// fragmentLinker.setOutputParams(_mulLightingProcedure, "tColor");
// }
// if (fogMode == SIMPLE) {
// vertexLinker.addProcedure(passSimpleFogConstProcedure);
// vertexLinker.setInputParams(passSimpleFogConstProcedure, positionVar);
// fragmentLinker.addProcedure(outputWithSimpleFogProcedure);
// fragmentLinker.setInputParams(outputWithSimpleFogProcedure, "tColor");
// outputProcedure = outputWithSimpleFogProcedure;
// } else if (fogMode == ADVANCED) {
// vertexLinker.declareVariable("tProjected");
// vertexLinker.setOutputParams(_projectProcedure, "tProjected");
// vertexLinker.addProcedure(postPassAdvancedFogConstProcedure);
// vertexLinker.setInputParams(postPassAdvancedFogConstProcedure, positionVar, "tProjected");
// fragmentLinker.addProcedure(outputWithAdvancedFogProcedure);
// fragmentLinker.setInputParams(outputWithAdvancedFogProcedure, "tColor");
// outputProcedure = outputWithAdvancedFogProcedure;
// }
fragmentLinker.varyings = vertexLinker.varyings;
program = new TileIndexMaterialProgram(vertexLinker, fragmentLinker, (shadowedLight != null) ? 1 : lightsLength);
program.upload(camera.context3D);
programs[key] = program;
}
return program;
}
private function addDrawUnits(program:TileIndexMaterialProgram, camera:Camera3D, surface:Surface, geometry:Geometry, opacityMap:TextureResource, lights:Vector.<Light3D>, lightsLength:int, isFirstGroup:Boolean, shadowedLight:Light3D, opaqueOption:Boolean, transparentOption:Boolean, objectRenderPriority:int):void {
// Buffers
var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
var uvBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[0]);
var normalsBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.NORMAL);
var tangentsBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TANGENT4);
if (positionBuffer == null || uvBuffer == null) return;
if ((lightsLength > 0 || shadowedLight != null) && (normalsBuffer == null || tangentsBuffer == null)) return;
var object:Object3D = surface.object;
// Draw call
var drawUnit:DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program);
// Streams
drawUnit.setVertexBufferAt(program.aPosition, positionBuffer, geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]);
drawUnit.setVertexBufferAt(program.aUV, uvBuffer, geometry._attributesOffsets[VertexAttributes.TEXCOORDS[0]], VertexAttributes.FORMATS[VertexAttributes.TEXCOORDS[0]]);
// Constants
object.setTransformConstants(drawUnit, surface, program.vertexShader, camera);
drawUnit.setProjectionConstants(camera, program.cProjMatrix, object.localToCameraTransform);
// Set options for a surface. X should be 0.
drawUnit.setFragmentConstantsFromNumbers(program.cSurface, 0, glossiness, specularPower, 1);
drawUnit.setFragmentConstantsFromNumbers(program.cThresholdAlpha, alphaThreshold, 0, 0, alpha);
// mine constants
drawUnit.setFragmentConstantsFromNumbers(program.cTilePaddSize, tilePaddingU, tilePaddingV, camera.focalLength, 2 ); // last values for dummy testing
// lod constant
drawUnit.setFragmentConstantsFromNumbers(program.cTileFrac, 1 / tileIndexMaps[lookupIndex].data.width, 1 / tileIndexMaps[lookupIndex].data.height, tileSizeU, tileSizeV);
//
drawUnit.setFragmentConstantsFromNumbers(program.cF, camera.focalLength / resolution, totalMipmaps, mipmapUVCap, 1);
drawUnit.setFragmentConstantsFromNumbers(program.cAtlasTiles, 4, 0.25, 16, 1/16);
var light:Light3D;
var len:Number;
var transform:Transform3D;
var rScale:Number;
var omni:OmniLight;
var spot:SpotLight;
var falloff:Number;
var hotspot:Number;
if (lightsLength > 0 || shadowedLight != null) {
if (_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED) {
drawUnit.setVertexBufferAt(program.aNormal, normalsBuffer, geometry._attributesOffsets[VertexAttributes.NORMAL], VertexAttributes.FORMATS[VertexAttributes.NORMAL]);
drawUnit.setVertexBufferAt(program.aTangent, tangentsBuffer, geometry._attributesOffsets[VertexAttributes.TANGENT4], VertexAttributes.FORMATS[VertexAttributes.TANGENT4]);
}
drawUnit.setTextureAt(program.sBump, normalMap._texture);
var camTransform:Transform3D = object.cameraToLocalTransform;
drawUnit.setVertexConstantsFromNumbers(program.cCameraPosition, camTransform.d, camTransform.h, camTransform.l);
for (var i:int = 0; i < lightsLength; i++) {
light = lights[i];
if (light is DirectionalLight) {
transform = light.lightToObjectTransform;
len = Math.sqrt(transform.c*transform.c + transform.g*transform.g + transform.k*transform.k);
drawUnit.setFragmentConstantsFromNumbers(program.cPosition[i], -transform.c/len, -transform.g/len, -transform.k/len, 1);
drawUnit.setFragmentConstantsFromNumbers(program.cColor[i], light.red, light.green, light.blue);
} else if (light is OmniLight) {
omni = light as OmniLight;
transform = light.lightToObjectTransform;
rScale = Math.sqrt(transform.a*transform.a + transform.e*transform.e + transform.i*transform.i);
rScale += Math.sqrt(transform.b*transform.b + transform.f*transform.f + transform.j*transform.j);
rScale += Math.sqrt(transform.c*transform.c + transform.g*transform.g + transform.k*transform.k);
rScale /= 3;
drawUnit.setFragmentConstantsFromNumbers(program.cPosition[i], transform.d, transform.h, transform.l);
drawUnit.setFragmentConstantsFromNumbers(program.cRadius[i], 1, omni.attenuationEnd*rScale - omni.attenuationBegin*rScale, omni.attenuationBegin*rScale);
drawUnit.setFragmentConstantsFromNumbers(program.cColor[i], light.red, light.green, light.blue);
} else if (light is SpotLight) {
spot = light as SpotLight;
transform = light.lightToObjectTransform;
rScale = Math.sqrt(transform.a*transform.a + transform.e*transform.e + transform.i*transform.i);
rScale += Math.sqrt(transform.b*transform.b + transform.f*transform.f + transform.j*transform.j);
rScale += len = Math.sqrt(transform.c*transform.c + transform.g*transform.g + transform.k*transform.k);
rScale /= 3;
falloff = Math.cos(spot.falloff*0.5);
hotspot = Math.cos(spot.hotspot*0.5);
drawUnit.setFragmentConstantsFromNumbers(program.cPosition[i], transform.d, transform.h, transform.l);
drawUnit.setFragmentConstantsFromNumbers(program.cAxis[i], -transform.c/len, -transform.g/len, -transform.k/len);
drawUnit.setFragmentConstantsFromNumbers(program.cRadius[i], spot.attenuationEnd*rScale - spot.attenuationBegin*rScale, spot.attenuationBegin*rScale, hotspot == falloff ? 0.000001 : hotspot - falloff, falloff);
drawUnit.setFragmentConstantsFromNumbers(program.cColor[i], light.red, light.green, light.blue);
}
}
}
if (shadowedLight != null) {
light = shadowedLight;
if (light is DirectionalLight) {
transform = light.lightToObjectTransform;
len = Math.sqrt(transform.c*transform.c + transform.g*transform.g + transform.k*transform.k);
drawUnit.setFragmentConstantsFromNumbers(program.cPosition[0], -transform.c/len, -transform.g/len, -transform.k/len, 1);
drawUnit.setFragmentConstantsFromNumbers(program.cColor[0], light.red, light.green, light.blue);
} else if (light is OmniLight) {
omni = light as OmniLight;
transform = light.lightToObjectTransform;
rScale = Math.sqrt(transform.a*transform.a + transform.e*transform.e + transform.i*transform.i);
rScale += Math.sqrt(transform.b*transform.b + transform.f*transform.f + transform.j*transform.j);
rScale += Math.sqrt(transform.c*transform.c + transform.g*transform.g + transform.k*transform.k);
rScale /= 3;
drawUnit.setFragmentConstantsFromNumbers(program.cPosition[0], transform.d, transform.h, transform.l);
drawUnit.setFragmentConstantsFromNumbers(program.cRadius[0], 1, omni.attenuationEnd*rScale - omni.attenuationBegin*rScale, omni.attenuationBegin*rScale);
drawUnit.setFragmentConstantsFromNumbers(program.cColor[0], light.red, light.green, light.blue);
} else if (light is SpotLight) {
spot = light as SpotLight;
transform = light.lightToObjectTransform;
rScale = Math.sqrt(transform.a*transform.a + transform.e*transform.e + transform.i*transform.i);
rScale += Math.sqrt(transform.b*transform.b + transform.f*transform.f + transform.j*transform.j);
rScale += len = Math.sqrt(transform.c*transform.c + transform.g*transform.g + transform.k*transform.k);
rScale /= 3;
falloff = Math.cos(spot.falloff*0.5);
hotspot = Math.cos(spot.hotspot*0.5);
drawUnit.setFragmentConstantsFromNumbers(program.cPosition[0], transform.d, transform.h, transform.l);
drawUnit.setFragmentConstantsFromNumbers(program.cAxis[0], -transform.c/len, -transform.g/len, -transform.k/len);
drawUnit.setFragmentConstantsFromNumbers(program.cRadius[0], spot.attenuationEnd*rScale - spot.attenuationBegin*rScale, spot.attenuationBegin*rScale, hotspot == falloff ? 0.000001 : hotspot - falloff, falloff);
drawUnit.setFragmentConstantsFromNumbers(program.cColor[0], light.red, light.green, light.blue);
}
}
// Textures
drawUnit.setTextureAt(program.sDiffuse, diffuseMap._texture);
drawUnit.setTextureAt(program.sTileIndices, tileIndexMaps[lookupIndex]._texture);
drawUnit.setTextureAt(program.sMipmapOffsets, mipmapTable._texture);
if (opacityMap != null) {
drawUnit.setTextureAt(program.sOpacity, opacityMap._texture);
}
if (glossinessMap != null) {
drawUnit.setTextureAt(program.sGlossiness, glossinessMap._texture);
}
if (specularMap != null) {
drawUnit.setTextureAt(program.sSpecular, specularMap._texture);
}
if (isFirstGroup) {
if (lightMap != null) {
drawUnit.setVertexBufferAt(program.aUV1, geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[lightMapChannel]), geometry._attributesOffsets[VertexAttributes.TEXCOORDS[lightMapChannel]], Context3DVertexBufferFormat.FLOAT_2);
drawUnit.setFragmentConstantsFromNumbers(program.cAmbientColor, 0,0,0, 1);
drawUnit.setTextureAt(program.sLightMap, lightMap._texture);
} else {
drawUnit.setFragmentConstantsFromVector(program.cAmbientColor, camera.ambient, 1);
}
}
else{
drawUnit.setFragmentConstantsFromNumbers(program.cAmbientColor, 0,0,0, 1);
}
setPassUVProcedureConstants(drawUnit, program.vertexShader);
if (shadowedLight != null && ((shadowedLight is DirectionalLight)||(shadowedLight is OmniLight))) {
shadowedLight.shadow.setup(drawUnit, program.vertexShader, program.fragmentShader, surface);
}
// Inititalizing render properties
if (opaqueOption) {
// Use z-buffer within DrawCall, draws without blending
if (isFirstGroup){
drawUnit.blendSource = Context3DBlendFactor.ONE;
drawUnit.blendDestination = Context3DBlendFactor.ZERO;
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE);
}
else{
drawUnit.blendSource = Context3DBlendFactor.ONE;
drawUnit.blendDestination = Context3DBlendFactor.ONE;
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE_OVERHEAD);
}
}
if (transparentOption){
// Do not use z-buffer, draws with blending
if (isFirstGroup){
drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA;
drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
}
else{
drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA;
drawUnit.blendDestination = Context3DBlendFactor.ONE;
}
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT);
}
var lm:Transform3D = object.localToCameraTransform;
drawUnit.setVertexConstantsFromNumbers(program.vertexShader.getVariableIndex("cZSpace"), lm.i, lm.j, lm.k, lm.l);
// if (fogMode == SIMPLE || fogMode == ADVANCED) {
// var lm:Transform3D = object.localToCameraTransform;
// var dist:Number = fogFar - fogNear;
// drawUnit.setVertexConstantsFromNumbers(program.vertexShader.getVariableIndex("cFogSpace"), lm.i/dist, lm.j/dist, lm.k/dist, (lm.l - fogNear)/dist);
// drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cFogRange"), fogMaxDensity, 1, 0, 1 - fogMaxDensity);
// }
// if (fogMode == SIMPLE) {
// drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cFogColor"), fogColorR, fogColorG, fogColorB);
// }
// if (fogMode == ADVANCED) {
// if (fogTexture == null) {
// var bmd:BitmapData = new BitmapData(32, 1, false, 0xFF0000);
// for (i = 0; i < 32; i++) {
// bmd.setPixel(i, 0, ((i/32)*255) << 16);
// }
// fogTexture = new BitmapTextureResource(bmd);
// fogTexture.upload(camera.context3D);
// }
// var cLocal:Transform3D = camera.localToGlobalTransform;
// var halfW:Number = camera.view.width/2;
// var leftX:Number = -halfW*cLocal.a + camera.focalLength*cLocal.c;
// var leftY:Number = -halfW*cLocal.e + camera.focalLength*cLocal.g;
// var rightX:Number = halfW*cLocal.a + camera.focalLength*cLocal.c;
// var rightY:Number = halfW*cLocal.e + camera.focalLength*cLocal.g;
// // Finding UV
// var angle:Number = (Math.atan2(leftY, leftX) - Math.PI/2);
// if (angle < 0) angle += Math.PI*2;
// var dx:Number = rightX - leftX;
// var dy:Number = rightY - leftY;
// var lens:Number = Math.sqrt(dx*dx + dy*dy);
// leftX /= lens;
// leftY /= lens;
// rightX /= lens;
// rightY /= lens;
// var uScale:Number = Math.acos(leftX*rightX + leftY*rightY)/Math.PI/2;
// var uRight:Number = angle/Math.PI/2;
//
// drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cFogConsts"), 0.5*uScale, 0.5 - uRight, 0);
// drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sFogTexture"), fogTexture._texture);
// }
}
private static var lightGroup:Vector.<Light3D> = new Vector.<Light3D>();
private static var shadowGroup:Vector.<Light3D> = new Vector.<Light3D>();
/**
* @private
*/
override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector.<Light3D>, lightsLength:int, useShadow:Boolean, objectRenderPriority:int = -1):void {
lightMap = lookupIndex < lightMaps.length ? lightMaps[ lookupIndex ] : null;
if (diffuseMap == null || normalMap == null || diffuseMap._texture == null || normalMap._texture == null) return;
// Check if textures uploaded in to the context.
if (opacityMap != null && opacityMap._texture == null || glossinessMap != null && glossinessMap._texture == null || specularMap != null && specularMap._texture == null || lightMap != null && lightMap._texture == null) return;
var object:Object3D = surface.object;
// Buffers
var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
var uvBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[0]);
var normalsBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.NORMAL);
var tangentsBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TANGENT4);
if (positionBuffer == null || uvBuffer == null) return;
var i:int;
var light:Light3D;
if (lightsLength > 0 && (_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED)) {
if (normalsBuffer == null || tangentsBuffer == null) return;
}
// Refresh programs for this context.
if (camera.context3D != cachedContext3D) {
cachedContext3D = camera.context3D;
programsCache = caches[cachedContext3D];
if (programsCache == null) {
programsCache = new Dictionary(false);
caches[cachedContext3D] = programsCache;
}
}
var optionsPrograms:Array = programsCache[object.transformProcedure];
if (optionsPrograms == null) {
optionsPrograms = [];
programsCache[object.transformProcedure] = optionsPrograms;
}
// Form groups of lights
var groupsCount:int = 0;
var lightGroupLength:int = 0;
var shadowGroupLength:int = 0;
for (i = 0; i < lightsLength; i++) {
light = lights[i];
if (light.shadow != null && useShadow) {
shadowGroup[int(shadowGroupLength++)] = light;
} else {
if (lightGroupLength == 6) {
groups[int(groupsCount++)] = lightGroup;
lightGroup = new Vector.<Light3D>();
lightGroupLength = 0;
}
lightGroup[int(lightGroupLength++)] = light;
}
}
if (lightGroupLength != 0) {
groups[int(groupsCount++)] = lightGroup;
}
// Iterate groups
var materialKey:int;
var program:TileIndexMaterialProgram;
var omniLightCount:int = 0;
var directionalLightCount:int = 0;
var spotLightCount:int = 0;
if (groupsCount == 0 && shadowGroupLength == 0) {
// There is only Ambient light on the scene
// Form key
materialKey = ((lightMap != null) ? LIGHT_MAP_BIT : 0) | ((glossinessMap != null) ? GLOSSINESS_MAP_BIT : 0) | ((specularMap != null) ? SPECULAR_MAP_BIT : 0);
if (opaquePass && alphaThreshold <= alpha) {
if (alphaThreshold > 0) {
// Alpha test
// use opacityMap if it is presented
program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 1, null, 0, true, null);
addDrawUnits(program, camera, surface, geometry, opacityMap, null, 0, true, null, true, false, objectRenderPriority);
} else {
// do not use opacityMap at all
program = getProgram(object, optionsPrograms, camera, materialKey, null, 0, null, 0, true, null);
addDrawUnits(program, camera, surface, geometry, null, null, 0, true, null, true, false, objectRenderPriority);
}
}
// Transparent pass
if (transparentPass && alphaThreshold > 0 && alpha > 0) {
// use opacityMap if it is presented
if (alphaThreshold <= alpha && !opaquePass) {
// Alpha threshold
program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 2, null, 0, true, null);
addDrawUnits(program, camera, surface, geometry, opacityMap, null, 0, true, null, false, true, objectRenderPriority);
} else {
// There is no Alpha threshold or check z-buffer by previous pass
program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 0, null, 0, true, null);
addDrawUnits(program, camera, surface, geometry, opacityMap, null, 0, true, null, false, true, objectRenderPriority);
}
}
} else {
var j:int;
var isFirstGroup:Boolean = true;
for (i = 0; i < groupsCount; i++) {
lightGroup = groups[i];
lightGroupLength = lightGroup.length;
// Group of lights without shadow
// Form key
materialKey = (isFirstGroup) ? ((lightMap != null) ? LIGHT_MAP_BIT : 0) : 0;
materialKey |= (_normalMapSpace << NORMAL_MAP_SPACE_OFFSET) | ((glossinessMap != null) ? GLOSSINESS_MAP_BIT : 0) | ((specularMap != null) ? SPECULAR_MAP_BIT : 0);
for (j = 0; j < lightGroupLength; j++) {
light = lightGroup[j];
if (light is OmniLight) omniLightCount++; else if (light is DirectionalLight) directionalLightCount++; else if (light is SpotLight) spotLightCount++;
}
materialKey |= omniLightCount << OMNI_LIGHT_OFFSET;
materialKey |= directionalLightCount << DIRECTIONAL_LIGHT_OFFSET;
materialKey |= spotLightCount << SPOT_LIGHT_OFFSET;
// Create program and drawUnit for group
// Opaque pass
if (opaquePass && alphaThreshold <= alpha) {
if (alphaThreshold > 0) {
// Alpha test
// use opacityMap if it is presented
program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 1, lightGroup, lightGroupLength, isFirstGroup, null);
addDrawUnits(program, camera, surface, geometry, opacityMap, lightGroup, lightGroupLength, isFirstGroup, null, true, false, objectRenderPriority);
} else {
// do not use opacityMap at all
program = getProgram(object, optionsPrograms, camera, materialKey, null, 0, lightGroup, lightGroupLength, isFirstGroup, null);
addDrawUnits(program, camera, surface, geometry, null, lightGroup, lightGroupLength, isFirstGroup, null, true, false, objectRenderPriority);
}
}
// Transparent pass
if (transparentPass && alphaThreshold > 0 && alpha > 0) {
// use opacityMap if it is presented
if (alphaThreshold <= alpha && !opaquePass) {
// Alpha threshold
program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 2, lightGroup, lightGroupLength, isFirstGroup, null);
addDrawUnits(program, camera, surface, geometry, opacityMap, lightGroup, lightGroupLength, isFirstGroup, null, false, true, objectRenderPriority);
} else {
// There is no Alpha threshold or check z-buffer by previous pass
program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 0, lightGroup, lightGroupLength, isFirstGroup, null);
addDrawUnits(program, camera, surface, geometry, opacityMap, lightGroup, lightGroupLength, isFirstGroup, null, false, true, objectRenderPriority);
}
}
isFirstGroup = false;
lightGroup.length = 0;
}
if (shadowGroupLength > 0) {
// Group of ligths with shadow
// For each light we will create new drawUnit
for (j = 0; j < shadowGroupLength; j++) {
light = shadowGroup[j];
// Form key
materialKey = (isFirstGroup) ? ((lightMap != null) ? LIGHT_MAP_BIT : 0) : 0;
materialKey |= (_normalMapSpace << NORMAL_MAP_SPACE_OFFSET) | ((glossinessMap != null) ? GLOSSINESS_MAP_BIT : 0) | ((specularMap != null) ? SPECULAR_MAP_BIT : 0);
materialKey |= light.shadow.type << SHADOW_OFFSET;
if (light is OmniLight) materialKey |= 1 << OMNI_LIGHT_OFFSET; else if (light is DirectionalLight) materialKey |= 1 << DIRECTIONAL_LIGHT_OFFSET; else if (light is SpotLight) materialKey |= 1 << SPOT_LIGHT_OFFSET;
// Для группы создаем программу и дроуюнит
// Opaque pass
if (opaquePass && alphaThreshold <= alpha) {
if (alphaThreshold > 0) {
// Alpha test
// use opacityMap if it is presented
program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 1, null, 0, isFirstGroup, light);
addDrawUnits(program, camera, surface, geometry, opacityMap, null, 0, isFirstGroup, light, true, false, objectRenderPriority);
} else {
// do not use opacityMap at all
program = getProgram(object, optionsPrograms, camera, materialKey, null, 0, null, 0, isFirstGroup, light);
addDrawUnits(program, camera, surface, geometry, null, null, 0, isFirstGroup, light, true, false, objectRenderPriority);
}
}
// Transparent pass
if (transparentPass && alphaThreshold > 0 && alpha > 0) {
// use opacityMap if it is presented
if (alphaThreshold <= alpha && !opaquePass) {
// Alpha threshold
program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 2, null, 0, isFirstGroup, light);
addDrawUnits(program, camera, surface, geometry, opacityMap, null, 0, isFirstGroup, light, false, true, objectRenderPriority);
} else {
// There is no Alpha threshold or check z-buffer by previous pass
program = getProgram(object, optionsPrograms, camera, materialKey, opacityMap, 0, null, 0, isFirstGroup, light);
addDrawUnits(program, camera, surface, geometry, opacityMap, null, 0, isFirstGroup, light, false, true, objectRenderPriority);
}
}
isFirstGroup = false;
}
}
shadowGroup.length = 0;
}
groups.length = 0;
}
/**
* @inheritDoc
*/
override public function clone():Material {
var res:TileIndexMaterial = new TileIndexMaterial(atlasSheet, mipmapTable, mipmapUVCap, tileIndexMaps, normalMap, lightMaps, uvTransformRef, tileSizePx, tilePaddingPxH, tilePaddingPxV);
res.clonePropertiesFrom(this);
return res;
}
/**
* @inheritDoc
*/
override protected function clonePropertiesFrom(source:Material):void {
super.clonePropertiesFrom(source);
var sMaterial:TileIndexMaterial = TileIndexMaterial(source);
glossiness = sMaterial.glossiness;
specularPower = sMaterial.specularPower;
_normalMapSpace = sMaterial._normalMapSpace;
lightMap = sMaterial.lightMap;
lightMapChannel = sMaterial.lightMapChannel;
resolution = sMaterial.resolution;
}
}
/* ROAD MAP:
Sample t1.xyz
-----------------
t1.xyz - the tile sample rgb (refer to bit layout for t1 tile)
Find t0.xy: (transformed uv mipmapped offset on atlas)
----------
// (t0.zw free, t1.w free)
t2.xyzw - unpack t1.z transform to get uv transform matrix
t0.xy - find normalized uv ratio -> transform it by matrix
// (t0.zw and t2.w free)
t2.xyz - reuse variable now for the mipmapped sample,
// (t1.z free)
t0.xy -> -> translate to atlas scale + mipmapped uv offset
Find t0-t2.w, from t2.xyz: blend intensities
-----------------------------------
t2.xyz - sample from t1.xyz over blend map
mov t2.x, t0.w
mov t2.y, t1.w
mov t2.z, t2.w
Finalise t2.xyzw output color
-----------------
Sample R texture color from t1, multiply by t0.www, store into t2.xyz (t0.w free)
Sample G texture color from t1, multiply by t1.www, add into t2.xyz (t1.w free)
Sample B texture color from t1, multiply by t2.www, add into t2.xyz (t2.w free)
mov constant 1 back to t2.w for output color t2.xyzw
_________________________________________________________________________
EXTRACTING DATA within a RGB color sample:
Bit layout per t1 tile
-----------------------------------------
4x4 textures: (16 textures to select from)
--------------
Red - RRRRGGGG
Green - BBBBkkkk
Blue - abcdefgh // Once t0 is found, t1.z can be used as a temporary slot
_________________________________________________________________________
To get upper and lower bit ratios:
w = ratio * 2^bits shifted
k = frc(w) - for lower bit ratio;
w = w - k;
w / 2^bits shifted - for upper bits ratio
_______________________________________
Given a decimal ratio spanning across 2 bits, determine upper bit and lower bit
respectively in terms of 0 or 1. This is the final prorcess which can easily be
simplified as:
0 00 0/4
0.25 01 1/4
.5 10 2/4
.75 11 3/4
Math.round( ratio ) // for upper bit, if ratio is lower than .5, return 0,
else return 1
frc(ratio*2) != 0 ? 1 : 0 // for lower bit, if remainder of ratio*2 is found,
return 1, else return 0.
*/