Saboteur Jetty
Counting number of cardgame path combinations for Saboteur version 1 and version 2.
Includes 3d version preview soon as I have a 3d model prepared!
https://en.wikipedia.org/wiki/Saboteur_%28card_game%29
/**
* Copyright Glidias ( http://wonderfl.net/user/Glidias )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/huTi
*/
package
{
import alternativa.engine3d.core.BoundBox;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.loaders.ParserA3D;
import alternativa.engine3d.loaders.ParserMaterial;
import alternativa.engine3d.loaders.TexturesLoader;
import alternativa.engine3d.materials.FillMaterial;
import alternativa.engine3d.objects.Surface;
import alternativa.engine3d.resources.ExternalTextureResource;
import alternativa.engine3d.utils.Object3DUtils;
import com.bit101.components.Label;
import com.bit101.components.NumericStepper;
import com.bit101.components.TextArea;
import com.bit101.components.VBox;
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.materials.TextureMaterial;
import alternativa.engine3d.materials.VertexLightTextureMaterial;
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.Sprite;
import flash.display.DisplayObject;
import flash.events.*
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.ui.Keyboard;
import flash.utils.ByteArray;
import flash.utils.Dictionary;
use namespace alternativa3d;
/**
* Counting number of cardgame path combinations for Saboteur version 1.
* Includes 3d version preview soon as I have a 3d model prepared!
* @author Glenn Ko
*/
public class SaboteurJetty extends Sprite
{
// standard mask values (fill regions paths)
public static const EAST:uint = (1 << 0);
public static const NORTH:uint = (1 << 1);
public static const WEST:uint = (1 << 2);
public static const SOUTH:uint = (1 << 3);
public static const NORTHEAST:uint = (1 << 0);
public static const NORTHWEST:uint = (1 << 1);
public static const SOUTHWEST:uint = (1 << 2);
public static const SOUTHEAST:uint = (1 << 3);
public static const NORTH_EAST:uint = (NORTH | EAST);
public static const NORTH_WEST:uint = (NORTH | WEST);
public static const SOUTH_WEST:uint = (SOUTH | WEST);
public static const SOUTH_EAST:uint = (SOUTH | EAST);
public static const ARC_VERTICAL:uint = (1 << 0);
public static const ARC_HORIZONTAL:uint = (1 << 1);
public static const ARC_NORTH_EAST:uint = (1 << 2);
public static const ARC_NORTH_WEST:uint = (1 << 3);
public static const ARC_SOUTH_WEST:uint = (1 << 4);
public static const ARC_SOUTH_EAST:uint = (1 << 5);
public static const ARC_MASK:uint = ~15;
static public const ARC_SHIFT:uint = 4;
// predicted: 2^5 (standard 90deg east,north,west,south,center mask) = 32
// + 8 (diagonal steer bendey cards, 2 steering mirrors and 6 with-orphan portions included)
// + 6 (vertical flip cases of diagonal steer bendey cards above 8-2=6, since 2 steering mirrors can't flip vertically) +
// + 6 ( horizontal and vertical striaght road with stray bit at side)
// + 4 (t junction with orphan edge bit)
// -1 (empty standard case)
// -1 (standard center cell only filled ) = 54
// -4 (standard single edge with center filled) (merely deeper end, achieves same purpose)
// = 50!
private var combinations:Vector.<uint> = new Vector.<uint>();
private var _template:Template
private const RADIAN:Number = Math.PI / 180;
private var ASSET_PATH:String;
public function SaboteurJetty()
{
Wonderfl.disable_capture();
var localMode:Boolean = (loaderInfo.url.indexOf("file://") >= 0);
localMode = false;
ASSET_PATH = (localMode ? "" : "http://glidias.github.io/Asharena/") + "assets/";
collectNumCombinations();
setup3DView();
_previewJetty = new PreviewJetty()
_previewJetty.x = 10;
_previewJetty.y = 10;
addChild(_previewJetty);
createStepper();
var loader:URLLoader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE, onURLBinaryLoadComplete);
loader.load( new URLRequest(ASSET_PATH + "models/jetties/bridge_all.a3d") );
}
private function onURLBinaryLoadComplete(e:Event):void
{
var loader:URLLoader = (e.currentTarget as URLLoader);
handleModelLoaded( loader.data );
}
private function handleModelLoaded(byteArray:ByteArray):void {
var parserA3D:ParserA3D = new ParserA3D();
parserA3D.parse( byteArray);
var objects:Vector.<Object3D> = parserA3D.objects;
var sLen:int = objects.length;
var sk:Mesh;
var textures:Vector.<ExternalTextureResource> = new Vector.<ExternalTextureResource>(); //create a vector ExternalTextureResource
var diffuse:ExternalTextureResource = new ExternalTextureResource(ASSET_PATH + "models/jetties/001.jpg");
var dummy:BitmapTextureResource = new BitmapTextureResource(new BitmapData(2, 2, false, 0xFF0000));
var injectMaterial:StandardMaterial = new StandardMaterial(diffuse, normalResource);
dummy.upload(_template.stage3D.context3D);
diffuse.upload(_template.stage3D.context3D);
textures.push(diffuse);
for (var s:int = 0; s < sLen; s++) {
sk = objects[s] as Mesh;
if (sk == null) continue;
sk.geometry.calculateNormals();
sk.geometry.calculateTangents(0);
sk.geometry.upload(_template.stage3D.context3D);
for (var i:int = 0; i < sk.numSurfaces; i++){ //cycle through all surface
var surface:Surface = sk.getSurface(i); //get the current surface
var material:ParserMaterial = surface.material as ParserMaterial; //a material property, we obtain ParserMaterial (for materials in Section 1.3)
/*
if (material != null) { //if the material is there, not null
var diffuse:ExternalTextureResource = material.textures["diffuse"]; //Create TextureResource-is the base class for all texture resources
if (diffuse != null){ //if there is texture
textures.push(diffuse); //add a vector with ExternalTextureResource
diffuse.url = ASSET_PATH + "models/jetties/001.jpg"; //skinTexturePath + diffuse.url;
surface.material = injectMaterial = new StandardMaterial(diffuse); //and assign the surface
}
}
*/
surface.material = injectMaterial; // new FillMaterial(0xFF0000, 1);
}
// _template.scene.addChild(sk);
//sk.boundBox = null;
}
var texturesLoader:TexturesLoader = new TexturesLoader(_template.stage3D.context3D);
_textureLoader = texturesLoader;
texturesLoader.loadResources(textures); //load the textures in the context
_template.controlObject.addChild(_jettyContainer=parserA3D.hierarchy[0]);
var bounds:BoundBox = Object3DUtils.calculateHierarchyBoundBox(_jettyContainer, _floor);
_floor.z = _jettyContainer.boundBox.minZ;
bounds.minX += 1;
bounds.minY += 1;
bounds.maxX -= 1;
bounds.maxY -= 1;
_floor.scaleX = bounds.minX / _floor.boundBox.minX;
_floor.scaleY = bounds.minY / _floor.boundBox.minY;
_template.rootControl.scaleX = 4;
_template.rootControl.scaleY = 4;
_template.rootControl.scaleZ = 4;
onStep(null, _stepper);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
}
private var _scrnie:Bitmap;
private function onKeyDown(e:KeyboardEvent):void
{
if (e.keyCode === Keyboard.F6) {
_template.takeScreenshot(screenieMethod);
}
else if (e.keyCode === Keyboard.F7) {
_scrnie= _template.takeScreenshot(screenieMethod2);
}
}
private function screenieMethod():Boolean
{
// Wonderfl.capture(); //
return true;
}
private function screenieMethod2():Boolean
{
stage.addEventListener(MouseEvent.CLICK, removeScreenie);
return false;
}
private function removeScreenie(e:Event=null):void {
if (_scrnie == null) return;
stage.removeEventListener(MouseEvent.CLICK, removeScreenie);
_scrnie.parent.removeChild(_scrnie);
_scrnie = null;
}
private function setup3DView():void
{
_template = new Template();
_template.addEventListener(Template.VIEW_CREATE, initialize);
addChild(_template);
}
private function initialize(event:Event):void {
_template.removeEventListener(Template.VIEW_CREATE, initialize);
//マテリアル用のリソースの用意
var bmd:BitmapData = new BitmapData(256, 256, true, 0xFF000000);
bmd.perlinNoise(64, 64, 1, 1, true , true);
var textureResource:BitmapTextureResource = new BitmapTextureResource(bmd);
//Textureの作成
var diffuseMap:BitmapData = new BitmapData(16, 16, false, 0xFF6666);
var normalMap:BitmapData = new BitmapData(16, 16, false, 0x8080FF);
//マテリアルの作成
var bitmapResource:BitmapTextureResource = new BitmapTextureResource(diffuseMap);
normalResource = new BitmapTextureResource(normalMap);
var materialA:TextureMaterial = new TextureMaterial(bitmapResource);
var materialB:TextureMaterial = new TextureMaterial(normalResource);
//var material:TextureMaterial = new TextureMaterial(bitmapResource);
var plane:Plane = new Plane(100, 100, 1, 1, true, false, materialA, materialB)
_template.controlObject.addChild(plane)
_template.initialize();
_floor = plane;
}
private function createStepper():void
{
var stepper:NumericStepper;
var vLayout:VBox = new VBox(this, 128);
vLayout.x = 128;
var label:Label = new Label(vLayout, 0, 0, (combinations.length) + " found.");
label.blendMode = "invert";
stepper = new NumericStepper(vLayout, 0, 0, onStep);
stepper.minimum = 0;
stepper.maximum = combinations.length - 1;
stepper.value = 0;
debugField = new TextArea(vLayout);
onStep(null, stepper);
_stepper = stepper;
}
private function onStep(e:Event, stepper:NumericStepper=null):void
{
var n:int;
var stepper:NumericStepper = e ? (e.currentTarget as NumericStepper) : stepper;
var value:uint = combinations[ stepper.value ];
var debugStr:String = "";
debugStr += (value & EAST) ? "E " : "";
debugStr += (value & NORTH) ? "N " : "";
debugStr += (value & WEST) ? "W " : "";
debugStr += (value & SOUTH) ? "S " : "";
var arcValue:uint = (value & ARC_MASK) >> ARC_SHIFT;
debugStr += (arcValue & ARC_VERTICAL) ? "vert " : "";
debugStr += (arcValue & ARC_HORIZONTAL) ? "horiz " : "";
debugStr += (arcValue & ARC_NORTH_EAST) ? "ne " : "";
debugStr += (arcValue & ARC_NORTH_WEST) ? "nw " : "";
debugStr += (arcValue & ARC_SOUTH_WEST) ? "sw " : "";
debugStr += (arcValue & ARC_SOUTH_EAST) ? "se " : "";
debugField.text = debugStr;
if (_previewJetty) {
n = _previewJetty.numChildren;
while (--n > -1) {
var d:DisplayObject = _previewJetty.getChildAt(n);
d.visible = visJetty(value, d.name, stepper.value);
}
}
if (_jettyContainer) {
for (var c:Object3D = _jettyContainer.childrenList; c != null; c = c.next) {
c.visible = visJetty(value, c.name, stepper.value);
}
}
}
private var arcEdgeDict:Dictionary = new Dictionary();
private var _previewJetty:Sprite;
private function getArcConnectionMaskValues2():Vector.<uint> { // 12 hardcoded arc combinations
var vec:Vector.<uint> = new <uint>[ // setup lookup table of 6 possible connections to check
// 0,
(ARC_VERTICAL),
(ARC_HORIZONTAL),
(ARC_VERTICAL | ARC_HORIZONTAL),
(ARC_NORTH_WEST | ARC_SOUTH_WEST),
(ARC_NORTH_EAST | ARC_SOUTH_EAST),
(ARC_NORTH_WEST | ARC_NORTH_EAST), // 5 - start use 90 degree junction
(ARC_SOUTH_WEST | ARC_SOUTH_EAST),
(ARC_NORTH_EAST),
(ARC_NORTH_WEST),
(ARC_SOUTH_WEST),
(ARC_SOUTH_EAST), // 10 - end use 90 degree junction
(ARC_NORTH_WEST | ARC_SOUTH_EAST),
(ARC_SOUTH_WEST | ARC_NORTH_EAST )
];
var result:uint;
var len:int = vec.length;
for (var i:int = 0; i < len; i++) {
var value:uint = vec[i];
result = 0;
result |= (value & ARC_VERTICAL) ? (NORTH | SOUTH) : 0;
result |= (value & ARC_HORIZONTAL) ? (WEST | EAST) : 0;
result |= (value & ARC_NORTH_EAST) ? (NORTH | EAST) : 0;
result |= (value & ARC_NORTH_WEST) ? (NORTH | WEST) : 0;
result |= (value & ARC_SOUTH_WEST) ? (SOUTH | WEST) : 0;
result |= (value & ARC_SOUTH_EAST) ? (SOUTH | EAST) : 0;
arcEdgeDict[value] = result;
}
//throw new Error(vec);
return vec;
}
//private var arcValueList:Array = []; // for debuggign
private var debugField:TextArea;
private var _textureLoader:TexturesLoader;
private var normalResource:BitmapTextureResource;
private var _floor:Plane;
private var _jettyContainer:Object3D;
private var _stepper:NumericStepper;
private function collectNumCombinations():void
{
var dict:Dictionary = new Dictionary();
var vec:Vector.<uint> = getArcConnectionMaskValues2();
var key:uint;
var count:uint = 0;
for (var i:uint = 1; i < 16; i++) { // go through activatable east,north,west,south edge states
for (var a:uint = 0; a < vec.length; a++) { // go through all activable arc combinations
var arcValue:uint = vec[a];
var mask:uint = arcEdgeDict[arcValue];
if ( (i & mask) != 0 && (i & mask) === mask ) { // case with valid connectable arc combination
key = (arcValue << ARC_SHIFT) | i;
//if (key == 0) throw new Error("WRONG1");
if (dict[key] == null) {
dict[ key] = count++;
combinations.push(key);
// arcValueList.push(arcValue);
}
}
// case without any connecting arc
///*
key = i;
//if (key == 0) throw new Error("WRONG2");
if (dict[key] == null) {
dict[ key] = count++;
combinations.push(key);
// arcValueList.push(0);
}
// */
}
}
}
private function visJetty(value:uint, groupName:String, index:int):Boolean {
var arcValue:uint = (value & ARC_MASK) >> ARC_SHIFT;
//if (arcValue != arcValueList[index]) throw new Error("MISMATCH!:"+arcValue + ", "+arcValueList[index]);
var edgeValue:uint = value & ~ARC_MASK;
var top90Deg:Boolean = arcValue === (ARC_NORTH_WEST | ARC_NORTH_EAST) || (arcValue === (ARC_NORTH_EAST) && edgeValue===NORTH_EAST) || (arcValue === (ARC_NORTH_WEST) && edgeValue === NORTH_WEST);
var bottom90Deg:Boolean = arcValue === (ARC_SOUTH_EAST | ARC_SOUTH_WEST) || (arcValue === (ARC_SOUTH_EAST) && edgeValue === SOUTH_EAST) || (arcValue === (ARC_SOUTH_WEST) && edgeValue === SOUTH_WEST);
var centerNarrow:Boolean = (arcValue === ARC_VERTICAL) && (edgeValue & (EAST | WEST)) != 0;
// /*
switch (groupName) {
case "side0":
case "side0_posts":
return ( value & EAST )!=0;
case "side1":
case "side1_posts":
return ( value & NORTH )!=0;
case "side2":
case "side2_posts":
return ( value & WEST )!=0;
case "side3":
case "side3_posts":
return ( value & SOUTH )!=0;
case "center":
return !centerNarrow && ((arcValue & (ARC_VERTICAL | ARC_HORIZONTAL))!=0) || top90Deg || bottom90Deg;
case "center_top":
return !centerNarrow && (arcValue & ARC_VERTICAL)!=0 || top90Deg; // has cut thru or T junction from bottom
case "center_bottom":
return !centerNarrow && (arcValue & ARC_VERTICAL)!=0 || bottom90Deg; // has cut thru or T junction from bottom
case "corner0_turn":
return (arcValue & ARC_NORTH_EAST)!=0 && !top90Deg; // and doesn't have T junction from top
case "corner1_turn":
return (arcValue & ARC_NORTH_WEST)!=0 && !top90Deg;
case "corner2_turn":
return (arcValue & ARC_SOUTH_WEST)!=0 && !bottom90Deg; // and doesn't have T junction from bottom
case "corner3_turn":
return (arcValue & ARC_SOUTH_EAST) != 0 && !bottom90Deg;
case "center_narrow":
return centerNarrow;
///*
case "corner0_railing":
return arcValue === (ARC_VERTICAL|ARC_HORIZONTAL) || ((arcValue & ARC_NORTH_EAST)!=0 && top90Deg);
case "corner1_railing":
return arcValue === (ARC_VERTICAL|ARC_HORIZONTAL) || ((arcValue & ARC_NORTH_WEST)!=0 && top90Deg);
case "corner2_railing":
return arcValue === (ARC_VERTICAL|ARC_HORIZONTAL) || ((arcValue & ARC_SOUTH_WEST)!=0 && bottom90Deg);
case "corner3_railing":
return arcValue === (ARC_VERTICAL|ARC_HORIZONTAL) || ((arcValue & ARC_SOUTH_EAST)!=0 && bottom90Deg);
//*/
default: return false;
}
// */
///return false;
}
}
}
import alternativa.engine3d.materials.A3DUtils;
import alternativa.engine3d.materials.LightMapMaterial;
import alternativa.engine3d.materials.Material;
import alternativa.engine3d.materials.NormalMapSpace;
import alternativa.engine3d.materials.TextureMaterial;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.Shape;
import flash.display.Sprite;
class PreviewJetty extends Sprite {
public function PreviewJetty() {
graphics.beginFill(0x000000);
graphics.drawRect(0, 0, 3 * 32, 5 * 32);
createShape("side0", 2, 2);
createShape("side1", 1, 0);
createShape("side2", 0, 2);
createShape("side3", 1, 4);
createShape("center", 1, 2);
createShape("center_top", 1, 1);
createShape("center_bottom", 1, 3);
createShape("corner0_turn", 4, 3, true);
createShape("corner0_turn", 4, 2, true);
createShape("corner0_turn", 4, 1, true);
createShape("corner1_turn", 1, 3, true);
createShape("corner1_turn", 1, 2, true);
createShape("corner1_turn", 1, 1, true);
createShape("corner2_turn", 1, 6, true);
createShape("corner2_turn", 1, 7, true);
createShape("corner2_turn", 1, 8, true);
createShape("corner3_turn", 4, 6, true);
createShape("corner3_turn", 4, 7, true);
createShape("corner3_turn", 4, 8, true);
createShape("center_narrow", 3, 2, true,-8);
createShape("center_narrow", 3, 3, true,-8);
createShape("center_narrow", 3, 4, true,-8);
createShape("center_narrow", 3, 5, true,-8);
createShape("center_narrow", 3, 6, true,-8);
createShape("center_narrow", 3, 7, true,-8);
}
private function createShape(name:String, x:int, y:int, halfSize:Boolean=false, dx:Number=0, dy:Number=0):void {
var gridSize:Number = halfSize ? 16 : 32;
var shape:Shape = new Shape();
shape.x = dx;
shape.y = dy;
shape.name = name;
shape.graphics.lineStyle(1, 0xFFFFFF);
shape.graphics.beginFill(0xFF0000);
shape.graphics.drawRect(x * gridSize, y * gridSize, gridSize, gridSize);
addChild(shape);
}
}
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 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 var rootControl:Object3D;
public static const VIEW_CREATE:String = 'view_create'
public var stage3D:Stage3D
public 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.backgroundColor = 0x666666;
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;
//コントロールオブジェクトの作成
rootControl = new Object3D();
// rootControl.rotationX = Math.PI * .5;
scene.addChild(rootControl);
controlObject = new Object3D()
rootControl.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 takeScreenshot( method:Function=null) : Bitmap //width:int, height:int,
{
var view:View = camera.view;
/*
var oldWidth:Number = view.width;
var oldHeight:Number = view.height;
view.width = width;
view.height = height;
*/
view.renderToBitmap = true;
camera.render(stage3D);
var canvas:BitmapData = view.canvas.clone();
// var bitmapData:BitmapData = view.canvas.clone();
view.renderToBitmap = false;
// view.width = oldWidth;
// view.height = oldHeight;
var child:Bitmap = new Bitmap(canvas);
stage.addChildAt( child,0 );
// take screenshot here
if (method!= null && method() ) {
stage.removeChild(child);
}
return child;
}
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);
}
}
}
/**
* 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/
* */
//package alternativa.engine3d.materials {
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.compiler.Linker;
import alternativa.engine3d.materials.compiler.Procedure;
import alternativa.engine3d.materials.compiler.VariableType;
import alternativa.engine3d.objects.Surface;
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;
/**
* Supports forceRenderPriority and some basic fixes to allow rendering without vertex normals.
*
* Material with diffuse, normal, opacity, specular maps and glossiness value. The material is able to draw skin
* with the number of bones in surface no more than 41. To reduce the number of bones in surface can break
* the skin for more surface with fewer bones. Use the method Skin.divide (). To be drawn with this material,
* geometry should have UV coordinates vertex normals and tangent and binormal values.
*
* @see alternativa.engine3d.core.VertexAttributes#TEXCOORDS
* @see alternativa.engine3d.core.VertexAttributes#NORMAL
* @see alternativa.engine3d.core.VertexAttributes#TANGENT4
* @see alternativa.engine3d.objects.Skin#divide()
*/
//public
class StandardMaterial 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
*/
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");
// 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");
/**
* @private
*/
alternativa3d static var fallbackTextureMaterial:TextureMaterial = new TextureMaterial();
/**
* @private
*/
alternativa3d static var fallbackLightMapMaterial:LightMapMaterial = new LightMapMaterial();
/**
* 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_RIGHT_HANDED;
*
* @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;
public var forceRenderPriority:int = -1;
/**
* Creates a new StandardMaterial instance.
* @param diffuseMap Diffuse map.
* @param normalMap Normal map.
* @param specularMap Specular map.
* @param glossinessMap Glossiness map.
* @param opacityMap Opacity map.
*/
public function StandardMaterial(diffuseMap:TextureResource = null, normalMap:TextureResource = null, specularMap:TextureResource = null, glossinessMap:TextureResource = null, opacityMap:TextureResource = null) {
super(diffuseMap, opacityMap);
this.normalMap = normalMap;
this.specularMap = specularMap;
this.glossinessMap = glossinessMap;
}
/**
* @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 (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;
}
}
/**
* @private
*/
alternativa3d function getPassUVProcedure():Procedure {
return _passUVProcedure;
}
/**
* @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):StandardMaterialProgram {
// 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:StandardMaterialProgram = 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(getPassUVProcedure());
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 StandardMaterialProgram(vertexLinker, fragmentLinker, (shadowedLight != null) ? 1 : lightsLength);
program.upload(camera.context3D);
programs[key] = program;
}
return program;
}
private function addDrawUnits(program:StandardMaterialProgram, 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);
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);
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);
}
// 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 {
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;
objectRenderPriority = forceRenderPriority < 0 ? objectRenderPriority : forceRenderPriority;
if (camera.context3DProperties.isConstrained) {
// fallback to simpler material
if (lightMap == null) {
fallbackTextureMaterial.diffuseMap = diffuseMap;
fallbackTextureMaterial.opacityMap = opacityMap;
fallbackTextureMaterial.alphaThreshold = alphaThreshold;
fallbackTextureMaterial.alpha = alpha;
fallbackTextureMaterial.opaquePass = opaquePass;
fallbackTextureMaterial.transparentPass = transparentPass;
fallbackTextureMaterial.collectDraws(camera, surface, geometry, lights, lightsLength, useShadow, objectRenderPriority);
} else {
fallbackLightMapMaterial.diffuseMap = diffuseMap;
fallbackLightMapMaterial.lightMap = lightMap;
fallbackLightMapMaterial.lightMapChannel = lightMapChannel;
fallbackLightMapMaterial.opacityMap = opacityMap;
fallbackLightMapMaterial.alphaThreshold = alphaThreshold;
fallbackLightMapMaterial.alpha = alpha;
fallbackLightMapMaterial.opaquePass = opaquePass;
fallbackLightMapMaterial.transparentPass = transparentPass;
fallbackLightMapMaterial.collectDraws(camera, surface, geometry, lights, lightsLength, useShadow, objectRenderPriority);
}
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:StandardMaterialProgram;
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);
var omniLightCount:int = 0;
var directionalLightCount:int = 0;
var spotLightCount:int = 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:StandardMaterial = new StandardMaterial(diffuseMap, normalMap, specularMap, glossinessMap, opacityMap);
res.clonePropertiesFrom(this);
return res;
}
/**
* @inheritDoc
*/
override protected function clonePropertiesFrom(source:Material):void {
super.clonePropertiesFrom(source);
var sMaterial:StandardMaterial = StandardMaterial(source);
glossiness = sMaterial.glossiness;
specularPower = sMaterial.specularPower;
_normalMapSpace = sMaterial._normalMapSpace;
lightMap = sMaterial.lightMap;
lightMapChannel = sMaterial.lightMapChannel;
}
}
//}
import alternativa.engine3d.materials.ShaderProgram;
import alternativa.engine3d.materials.compiler.Linker;
import flash.display3D.Context3D;
class StandardMaterialProgram 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 function StandardMaterialProgram(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");
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");
}
}
}