Saboteur Jetty Builder
forked from forked from: MeshUtility (diff: 3387)
Currently don't have pathfinding checks from start points yet, but would be relatively easy to implement. This does remind me of a sort-of Temple Run generator as well. https://en.wikipedia.org/wiki/Saboteur_%28card_game%29 https://www.youtube.com/watch?v=AXAIyEWunAM
ActionScript3 source code
/**
* Copyright Glidias ( http://wonderfl.net/user/Glidias )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/hcaJ
*/
package
{
import alternativa.engine3d.core.BoundBox;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Transform3D;
import alternativa.engine3d.loaders.ParserA3D;
import alternativa.engine3d.loaders.ParserMaterial;
import alternativa.engine3d.loaders.TexturesLoader;
import alternativa.engine3d.materials.FillMaterial;
import alternativa.engine3d.materials.Material;
import alternativa.engine3d.objects.Surface;
import alternativa.engine3d.primitives.Box;
import alternativa.engine3d.resources.ExternalTextureResource;
import alternativa.engine3d.utils.Object3DUtils;
import com.bit101.components.HBox;
import com.bit101.components.Label;
import com.bit101.components.NumericStepper;
import com.bit101.components.PushButton;
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.geom.Matrix3D;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.geom.Vector3D;
import flash.media.Camera;
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;
/**
* Construct a jetty path to your destination!
*
* @author Glenn Ko
*/
public class JettySaboteur3D extends Sprite
{
private var _template:Template
private const RADIAN:Number = Math.PI / 180;
private const DEGREE:Number = 180 / Math.PI;
// static public const DEFAULT_ROT_Z:Number =-Math.PI * .5;
// static public const DEFAULT_ROT_X:Number = Math.PI;
static private const SPEC_CAM_Z_OFF:Number = 290;
static private const COLOR_OCCUPIED:uint = 0x99AA44;
static private const COLOR_INVALID:uint = 0xFF6666;
static private const COLOR_VALID:uint = 0x00FF00;
static private const COLOR_OUTSIDE:uint = 0x333333;
static public const WORLD_SCALE:Number = 4;
private var ASSET_PATH:String;
private var pathUtil:SaboteurPathUtil;
private var cardinal:CardinalVectors = new CardinalVectors();
private var _pitchAmount:Number = 0;
private var _turnAmount:Number = 0;
private var _horizontalAmount:Number = 0;
private var _verticalAmount:Number = 0;
private var specCameraController:OrbitCameraController;
private var scrollRegion:Rectangle = new Rectangle();
private var trackGridPos:Boolean = true;
private var buildDict:Dictionary;
public function JettySaboteur3D()
{
Wonderfl.disable_capture();
var localMode:Boolean = (loaderInfo.url.indexOf("file://") >= 0);
localMode = false;
ASSET_PATH = (localMode ? "" : "http://glidias.github.io/Asharena/") + "assets/";
pathUtil = new SaboteurPathUtil();
buildDict = pathUtil.getNewDictionary();
setup3DView();
//_previewJetty = new PreviewJetty();
// addChild(_previewJetty);
//_previewJetty.y = 100;
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") );
stage.addEventListener(Event.RESIZE, onStageResize);
onStageResize();
}
private var _sw:Number;
private var _sh:Number;
private var _swPadd:Number;
private var _shPadd:Number;
private function onStageResize(e:Event=null):void
{
var xPadd:Number = .2 * stage.stageWidth;
var yPadd:Number = .2 * stage.stageHeight;
scrollRegion.x = xPadd;
scrollRegion.y = yPadd;
scrollRegion.width = stage.stageWidth - xPadd;
scrollRegion.height = stage.stageHeight - yPadd;
_swPadd = 1/xPadd;
_shPadd = 1/yPadd;
_sw =stage.stageWidth;
_sh =stage.stageHeight;
scrollSpeed.x = intendedScrollSpeed.x / _sw * .5;
scrollSpeed.y = intendedScrollSpeed.y / _sh * .5;
}
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);
injectMaterial.glossiness = 0;
injectMaterial.specularPower = 0;
dummy.upload(_template.stage3D.context3D);
diffuse.upload(_template.stage3D.context3D);
previewMaterial = injectMaterial.clone() as StandardMaterial;
previewMaterial.alphaThreshold = .99;
previewMaterial.alpha = .4;
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;
_floor.z = _jettyContainer.boundBox.maxZ - 4;
bounds.minX += .4;
bounds.minY += .4;
bounds.maxX -= .4;
bounds.maxY -= .4;
_gridSquareBound = bounds;
var xd:Number = bounds.maxX - bounds.minX;
var yd:Number = bounds.maxY - bounds.minY;
// var zd:Number = bounds.maxZ - bounds.minZ;
gridEastWidth =cardinal.getDist(cardinal.east, _gridSquareBound, 1);
gridSouthWidth =cardinal.getDist(cardinal.south, _gridSquareBound, 1);
//throw new Error(gridEastWidth + ", " + gridSouthWidth);
gridEastWidth_i = 1 / gridEastWidth;
gridSouthWidth_i = 1 / gridSouthWidth;
_floor.scaleX = bounds.minX / _floor.boundBox.minX;
_floor.scaleY = bounds.minY / _floor.boundBox.minY;
_template.rootControl.scaleX = WORLD_SCALE;
_template.rootControl.scaleY = WORLD_SCALE;
_template.rootControl.scaleZ = WORLD_SCALE;
onStep(null, _stepper);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
var dir:Vector3D;
//dir = cardinal.east;
testClone = _jettyContainer.clone();
//cardinal.set(dir, testClone, cardinal.getDist(dir, _gridSquareBound) );
_template.controlObject.addChild(testClone);
visJetty3DByValue(testClone, pathUtil.getValue(SaboteurPathUtil.EAST | SaboteurPathUtil.NORTH | SaboteurPathUtil.WEST | SaboteurPathUtil.SOUTH, SaboteurPathUtil.ARC_VERTICAL | SaboteurPathUtil.ARC_HORIZONTAL ) );
updateFloorPosition(0,0);
setMaterialToCont(previewMaterial, _jettyContainer);
//turnAmount = -.15 * Math.PI;
pitchAmount = -.24 * Math.PI;
specCameraController = new OrbitCameraController(_template.camera, _specFollowTarget, stage, stage, stage, false, true);
specCameraController.minDistance = .1;
specCameraController.setDistance(313, true);
specCameraController.maxAngleLatidude = 75;
specCameraController.minAngleLatitude = 10;
specCameraController.maxPitch = 0;
_template.controller = specCameraController;
//_specFollowTarget.addChild(_floor);
refreshCamPosition();
_template.preUpdate = preUpdate;
_template.preRender = preRender;
}
private function setMaterialToCont(mat:Material, cont:Object3D):void
{
for (var c:Object3D = cont.childrenList; c != null; c = c.next) {
var mesh:Mesh = c as Mesh;
if (mesh != null) {
mesh.setMaterialToAllSurfaces(mat);
}
}
}
private var gridEast:int = 0; // along east
private var gridSouth:int = 0 // along south
private var gridEastWidth:Number = 1;
private var gridSouthWidth:Number = 1;
private var gridEastWidth_i:Number = 1;
private var gridSouthWidth_i:Number = 1;
private function preUpdate():void
{
var mx:Number = mouseX;
var my:Number = mouseY;
if (autoScrollCamera && ( mx < scrollRegion.x || my < scrollRegion.y || mx > scrollRegion.width || my > scrollRegion.height) ) {
mx -= _sw * .5;
my -= _sh * .5;
translateCamera(
mx / _sw * scrollSpeed.x
,my / _sh * scrollSpeed.y
);
}
specCameraController.setDistance( (specCameraController._angleLatitude-specCameraController.minAngleLatitude)/(specCameraController.maxAngleLatidude - specCameraController.minAngleLatitude) * 600 + 155, true);
}
private function preRender():void {
//debugField.text = String( (specCameraController.maxAngleLatidude ) );
}
private function visJetty3DByIndex(obj:Object3D, index:uint):void {
index = pathUtil.combinations[ index ];
for (var c:Object3D = obj.childrenList; c != null; c = c.next) {
c.visible = pathUtil.visJetty(index, c.name);
}
}
private function visJetty3DByValue(obj:Object3D, value:uint):void {
for (var c:Object3D = obj.childrenList; c != null; c = c.next) {
c.visible = pathUtil.visJetty(value, c.name);
}
}
private var _scrnie:Bitmap;
private function onKeyDown(e:KeyboardEvent):void
{
var kc:uint = e.keyCode;
switch(kc) {
case Keyboard.F6:
_template.takeScreenshot(screenieMethod);
return;
case Keyboard.F7:
_scrnie= _template.takeScreenshot(screenieMethod2);
return;
case Keyboard.O:
pitchAmount += .01 * Math.PI;
return
case Keyboard.P:
pitchAmount -= .01 * Math.PI;
return;
case Keyboard.K:
turnAmount += .01 * Math.PI;
return;
case Keyboard.L:
turnAmount -= .01 * Math.PI;
return;
case Keyboard.UP:
verticalAmount--;
return;
case Keyboard.W: // diff
translateCamera(0, -8);
//verticalAmount--;
// _template.rootControl.x--;
return;
case Keyboard.DOWN:
verticalAmount++;
return;
case Keyboard.S: // diff
translateCamera(0, 8);
//verticalAmount++;
// _template.rootControl.x++;
return;
case Keyboard.LEFT:
horizontalAmount--;
return;
case Keyboard.A: // diff
//horizontalAmount--;
translateCamera(-8, 0);
return;
case Keyboard.RIGHT:
horizontalAmount++;
return;
case Keyboard.D: // diff
translateCamera( 8, 0);
//horizontalAmount++;
return;
case Keyboard.NUMPAD_ADD:
//_template.camera.z++;
specCameraController.setDistance(specCameraController.getDistance() + 1);
return;
case Keyboard.NUMPAD_SUBTRACT:
specCameraController.setDistance(specCameraController.getDistance() - 1);
return;
default:return;
}
}
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);
//camera.orthographic = true;
_template.rootControl.z = 254;
// _template.rootControl.rotationX = Math.PI;
// _template.rootControl.rotationZ = DEFAULT_ROT_Z;
//_template.rootControl.rotationY = -.45 * Math.PI;
}
private function initialize(event:Event):void {
_template.removeEventListener(Template.VIEW_CREATE, initialize);
_template.scene.addChild(_specFollowTarget);
// _specFollowTarget.addChild( new Box(10, 10, 10, 1, 1, 1, false, new FillMaterial(0xFF0000)));
//マテリアル用のリソースの用意
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);
materialB.alpha = .6;
materialB.alphaThreshold = .99;
normalResource.upload(_template.stage3D.context3D);
var plane:Plane = new Plane(100, 100, 1, 1, false, false,editorMat,editorMat);
_template.rootControl.addChild(plane)
_template.initialize();
_floor = plane;
}
private function createStepper():void
{
var stepper:NumericStepper;
uiSpr = new Sprite();
addChild(uiSpr);
var vLayout:VBox = new VBox(uiSpr, 0);
var hBox:HBox = new HBox(vLayout);
uiSpr.addEventListener(MouseEvent.ROLL_OVER, onVLayoutOver);
uiSpr.addEventListener(MouseEvent.ROLL_OUT, onVayoutOUt);
// var label:Label = new Label(vLayout, 0, 0, (pathUtil.combinations.length) + " found.");
// label.blendMode = "invert";
// label.y = -20;
stepper = new NumericStepper(hBox, 0, 0, onStep);
stepper.minimum = 0;
stepper.maximum = pathUtil.combinations.length - 1;
stepper.value = 0;
var buildBtn:PushButton = new PushButton(hBox, 0, 0, "Build", onBuildClick);
debugField = new TextArea(vLayout);
debugField.visible = false;
uiSpr.x = 30;
uiSpr.y = 30;
_stepper = stepper;
onStep(null, stepper);
addEventListener(Event.ENTER_FRAME, onEnterFrameRefresh);
}
private function onBuildClick(e:Event):void
{
if (checkBuildableResult() > 0) {
var value:uint = pathUtil.getValueByIndex(int(_stepper.value));
pathUtil.buildAt(buildDict, gridEast, gridSouth, value );
var newBuilding:Object3D = testClone.clone();
_template.controlObject.addChild( newBuilding);
visJetty3DByValue( newBuilding, value);
cardinal.transform(cardinal.east, newBuilding, cardinal.getDist(cardinal.east, _gridSquareBound)* gridEast );
cardinal.transform(cardinal.south, newBuilding, cardinal.getDist(cardinal.south, _gridSquareBound) * gridSouth );
checkBuildableResult();
}
}
private function onEnterFrameRefresh(e:Event):void
{
removeEventListener(Event.ENTER_FRAME, onEnterFrameRefresh);
uiSpr.graphics.beginFill(0xFF0000, 0);
uiSpr.graphics.drawRect(0, 0, uiSpr.width, uiSpr.height);
}
private function onVLayoutOver(e:MouseEvent):void
{
//trackGridPos = false;
autoScrollCamera = false;
}
private function onVayoutOUt(e:MouseEvent):void
{
// trackGridPos = true;
autoScrollCamera = true;
//checkGridPos();
}
private function onStep(e:Event, stepper:NumericStepper=null):void
{
var n:int;
var stepper:NumericStepper = e ? (e.currentTarget as NumericStepper) : stepper;
var value:uint = pathUtil.combinations[ stepper.value ];
var debugStr:String = "";
debugStr += (value & SaboteurPathUtil.EAST) ? "E " : "";
debugStr += (value & SaboteurPathUtil.NORTH) ? "N " : "";
debugStr += (value & SaboteurPathUtil.WEST) ? "W " : "";
debugStr += (value & SaboteurPathUtil.SOUTH) ? "S " : "";
var arcValue:uint = (value & SaboteurPathUtil.ARC_MASK) >> SaboteurPathUtil.ARC_SHIFT;
debugStr += (arcValue & SaboteurPathUtil.ARC_VERTICAL) ? "vert " : "";
debugStr += (arcValue & SaboteurPathUtil.ARC_HORIZONTAL) ? "horiz " : "";
debugStr += (arcValue & SaboteurPathUtil.ARC_NORTH_EAST) ? "ne " : "";
debugStr += (arcValue & SaboteurPathUtil.ARC_NORTH_WEST) ? "nw " : "";
debugStr += (arcValue & SaboteurPathUtil.ARC_SOUTH_WEST) ? "sw " : "";
debugStr += (arcValue & SaboteurPathUtil.ARC_SOUTH_EAST) ? "se " : "";
debugField.text = debugStr;
if (_previewJetty) {
n = _previewJetty.numChildren;
while (--n > -1) {
var d:DisplayObject = _previewJetty.getChildAt(n);
d.visible = pathUtil.visJetty(value, d.name);
}
}
if (_jettyContainer) {
for (var c:Object3D = _jettyContainer.childrenList; c != null; c = c.next) {
c.visible = pathUtil.visJetty(value, c.name);
}
}
checkBuildableResult();
}
//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 var _previewJetty:Sprite;
private var _gridSquareBound:BoundBox;
private var _specFollowTarget:Object3D = new Object3D();
private var scrollSpeed:Point = new Point(234, 324);
private var intendedScrollSpeed:Point = new Point(11222, 11222);
private var autoScrollCamera:Boolean=true;
private var uiSpr:Sprite;
private var editorMat:FillMaterial = new FillMaterial(COLOR_OCCUPIED, .3);
private var previewMaterial:StandardMaterial;
private var testClone:Object3D;
public function get pitchAmount():Number
{
return _pitchAmount;
}
public function set pitchAmount(value:Number):void
{
_pitchAmount = value;
}
public function get turnAmount():Number
{
return _turnAmount;
}
public function set turnAmount(value:Number):void
{
_turnAmount = value;
}
public function get horizontalAmount():Number
{
return _horizontalAmount;
}
public function set horizontalAmount(value:Number):void
{
_horizontalAmount = value;
refreshCamPosition();
}
public function get verticalAmount():Number
{
return _verticalAmount;
}
public function set verticalAmount(value:Number):void
{
_verticalAmount = value;
refreshCamPosition();
}
private function translateCamera(dispX:Number, dispY:Number):void {
if (_template.camera.transformChanged) {
_template.camera.composeTransforms();
}
var transform:Transform3D = _template.camera.transform;
var dx:Number = transform.a * dispX + transform.b * dispY;
var dy:Number = transform.e * dispX + transform.f * dispY;
var len:Number = 1 / Math.sqrt( dx * dx + dy * dy );
dx *= len;
dy *= len;
_specFollowTarget._x += dx;
_specFollowTarget._y +=dy;
_horizontalAmount = _specFollowTarget._x * cardinal.east.x + _specFollowTarget._y * cardinal.east.y + _specFollowTarget._z * cardinal.east.z;
_verticalAmount = _specFollowTarget._x * cardinal.south.x + _specFollowTarget._y * cardinal.south.y + _specFollowTarget._z * cardinal.south.z;
_specFollowTarget.transformChanged = true;
if (trackGridPos) checkGridPos();
}
private function checkGridPos():void {
var ge:int = Math.round ( _horizontalAmount / WORLD_SCALE * gridEastWidth_i);
var gs:int = Math.round( _verticalAmount / WORLD_SCALE * gridSouthWidth_i);
if (ge != gridEast || gs != gridSouth) updateFloorPosition(ge, gs);
}
private function updateFloorPosition(ge:int, gs:int):void
{
gridEast = ge;
gridSouth = gs;
//_floor.x = ge*gridEastWidth;
//_floor.y = gs * gridSouthWidth;
var eastD:Number = gridEastWidth * ge;
var southD:Number = gridSouthWidth * gs;
_floor._x = eastD * cardinal.east.x;
_floor._y = eastD * cardinal.east.y;
_floor._x += southD * cardinal.south.x;
_floor._y += southD * cardinal.south.y;
_jettyContainer._x = _floor._x;
_jettyContainer._y = _floor._y;
_jettyContainer.transformChanged = true;
_floor.transformChanged = true;
checkBuildableResult();
}
private function checkBuildableResult():int
{
var result:int = pathUtil.getValidResult(buildDict, gridEast, gridSouth, pathUtil.getValueByIndex(int(_stepper.value)) );
if (result === SaboteurPathUtil.RESULT_OCCUPIED) {
editorMat.color = COLOR_OCCUPIED;
//throw new Error(pathUtil.getGridKey(ge, gs));
}
else if (result === SaboteurPathUtil.RESULT_INVALID) {
editorMat.color = COLOR_INVALID;
}
else if (result === SaboteurPathUtil.RESULT_OUT) {
editorMat.color = COLOR_OUTSIDE;
}
else {
editorMat.color = COLOR_VALID;
}
return result;
}
private function refreshCamPosition():void
{
_template.camera._x = cardinal.east.x * _horizontalAmount;
_template.camera._y = cardinal.east.y * _horizontalAmount;
_template.camera._z = cardinal.east.z * _horizontalAmount;
_template.camera._x += cardinal.south.x * _verticalAmount;
_template.camera._y += cardinal.south.y * _verticalAmount;
_template.camera._z += cardinal.south.z * _verticalAmount;
_specFollowTarget._x = _template.camera._x;
_specFollowTarget._y = _template.camera._y;
_specFollowTarget._z = _template.camera._z + SPEC_CAM_Z_OFF;
_template.camera.transformChanged = true;
_specFollowTarget.transformChanged = true;
if (trackGridPos) checkGridPos();
}
private function calculateLocalToGlobal(obj:Object3D):Transform3D {
if (obj.transformChanged) obj.composeTransforms();
var trm:Transform3D = obj.localToGlobalTransform;
trm.copy(obj.transform);
var root:Object3D = obj;
while (root.parent != null) {
root = root.parent;
if (root.transformChanged) root.composeTransforms();
trm.append(root.transform);
}
return trm;
}
}
}
import alternativa.engine3d.core.BoundBox;
import flash.utils.Dictionary;
class CardinalVectors {
public var east:Vector3D = new Vector3D(0, -1, 0);
public var north:Vector3D = new Vector3D(1,0, 0);
public var west:Vector3D = new Vector3D(0, 1, 0);
public var south:Vector3D = new Vector3D(-1, 0, 0);
public function transform(vec:Vector3D, obj:Object3D, distance:Number):void {
obj.x += vec.x * distance;
obj.y += vec.y * distance;
obj.z += vec.z * distance;
}
public function set(vec:Vector3D, obj:Object3D, distance:Number):void {
obj.x = vec.x * distance;
obj.y = vec.y * distance;
obj.z = vec.z * distance;
}
public function getDist(vec:Vector3D, boundBox:BoundBox, numGridSquares:uint=1, scaler:Number=2):Number {
var val:Number = (boundBox.maxX * vec.x + boundBox.maxY * vec.y + boundBox.maxZ * vec.z);
val = val < 0 ? -val : val;
val *= numGridSquares;
val *= scaler;
return val;
}
}
class SaboteurPathUtil {
// 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!
public var combinations:Vector.<uint> = new Vector.<uint>();
private var arcEdgeDict:Dictionary = new Dictionary();
public static const RESULT_OUT:int = 0; // no neighbor or path
public static const RESULT_INVALID:int = -1; // can't connect validly
public static const RESULT_VALID:int = 1;
public static const RESULT_OCCUPIED:int = -2;
public function getValidResult(buildDict:Dictionary, east:int, south:int, value:uint):int {
if (buildDict[getGridKey(east, south)] != null) return RESULT_OCCUPIED;
var toNorth:uint = getGridKey(east, south - 1);
var toSouth:uint = getGridKey(east, south + 1);
var toWest:uint = getGridKey(east-1, south);
var toEast:uint = getGridKey(east + 1, south);
var neighborFlags:uint = 0;
neighborFlags |= buildDict[toEast] != null ? EAST : 0;
neighborFlags |= buildDict[toNorth] != null ? NORTH : 0;
neighborFlags |= buildDict[toWest] != null ? WEST : 0;
neighborFlags |= buildDict[toSouth] != null ? SOUTH : 0;
if (neighborFlags == 0) return RESULT_OUT;
// must have connecting node
var neighborVal:uint;
if (neighborFlags & EAST) {
neighborVal = buildDict[toEast];
if ( ((value & EAST) != 0) != ((neighborVal & WEST) != 0) ) return RESULT_INVALID;
}
if (neighborFlags & WEST) {
neighborVal = buildDict[toWest];
if ( ((value & WEST) != 0) != ((neighborVal & EAST) != 0) ) return RESULT_INVALID;
}
if (neighborFlags & NORTH) {
neighborVal = buildDict[toNorth];
if ( ((value & NORTH) != 0) != ((neighborVal & SOUTH) != 0) ) return RESULT_INVALID;
}
if (neighborFlags & SOUTH) {
neighborVal = buildDict[toSouth];
if ( ((value & SOUTH) != 0) != ((neighborVal & NORTH) != 0) ) return RESULT_INVALID;
}
return RESULT_VALID;
}
private static const INT_LIMIT:int = Math.sqrt(int.MAX_VALUE) * .5;
private var _dictValueToIndex:Dictionary;
public function getGridKey(east:int, south:int):uint {
return (south + INT_LIMIT) * INT_LIMIT * 2 + (east + INT_LIMIT);
}
public function buildAt(dict:Dictionary, east:int, south:int, value:uint):void {
dict[getGridKey(east, south)] = value;
}
public function getValue(availableSides:uint, availableArcs:uint):uint {
return availableSides | (availableArcs << ARC_SHIFT);
}
public function getValueByIndex(index:int):uint {
return combinations[index];
}
public function getIndexByValue(value:uint):int {
return _dictValueToIndex[value]!= null ? _dictValueToIndex[value] : -1;
}
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;
}
public function SaboteurPathUtil() {
collectNumCombinations();
}
private function collectNumCombinations():void
{
var dict:Dictionary = new Dictionary();
_dictValueToIndex = dict;
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);
}
// */
}
}
}
public function visJetty(value:uint, groupName:String):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;
}
public function getNewDictionary(startX:int = 0, startY:int=0 ):Dictionary
{
var dict:Dictionary = new Dictionary();
buildAt(dict, startX, startY, getValue(EAST|NORTH|WEST|SOUTH, ARC_HORIZONTAL|ARC_VERTICAL ) );
return dict;
}
}
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;
import flash.media.Camera;
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= new Object3D();
public static const VIEW_CREATE:String = 'view_create'
public var stage3D:Stage3D
public var camera:Camera3D
public var scene:Object3D
public var controlObject:Object3D;
public var controller:SimpleObjectController;
protected var directionalLight:DirectionalLight;
protected var ambientLight:AmbientLight;
//private var _previewCam:Camera3D:
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)
addChild(camera.diagram)
//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.rotationX = Math.PI * .5;
scene.addChild(rootControl);
controlObject = new Object3D()
rootControl.addChild(controlObject);
dispatchEvent(new Event(VIEW_CREATE));
//customiseScene();
}
public function initialize():void {
for each (var resource:Resource in scene.getResources(true)) {
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 {
if (preUpdate !=null) preUpdate();
if (controller) controller.update();
if (preRender != null) preRender();
camera.render(stage3D);
}
public var preRender:Function
public var preUpdate:Function;
}
/**
* 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");
}
}
}
//package alternativa.engine3d.controller {
import alternativa.engine3d.controllers.SimpleObjectController;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Object3D;
import flash.events.IEventDispatcher;
import flash.display.DisplayObjectContainer;
import flash.display.InteractiveObject;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Vector3D;
import flash.ui.Keyboard;
import flash.ui.Mouse;
import flash.ui.MouseCursor;
/**
* GeoCameraController は 3D オブジェクトの周りに配置することのできるコントローラークラスです。
* 緯度・経度で配置することができます。
*
* @author narutohyper
* @author clockmaker
*
* @see http://wonderfl.net/c/fwPU
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
*/
// public
class OrbitCameraController extends SimpleObjectController
{
//----------------------------------------------------------
//
// Static Property
//
//----------------------------------------------------------
/** 中心と方向へ移動するアクションを示す定数です。 */
public static const ACTION_FORWARD:String = "actionForward";
/** 中心と反対方向へ移動するアクションを示す定数です。 */
public static const ACTION_BACKWARD:String = "actionBackward";
/** イージングの終了判断に用いるパラメーターです。0〜0.2で設定し、0に近いほどイージングが残されます。 */
private static const ROUND_VALUE:Number = 0.1;
private var _lockRotationZ:Boolean = false;
private var _mouseWheelHandler:Function;
//----------------------------------------------------------
//
// Constructor
//
//----------------------------------------------------------
/**
* 新しい GeoCameraController インスタンスを作成します。
* @param targetObject コントローラーで制御したいオブジェクトです。
* @param mouseDownEventSource マウスダウンイベントとひもづけるオブジェクトです。
* @param mouseUpEventSource マウスアップイベントとひもづけるオブジェクトです。推奨は stage です。
* @param keyEventSource キーダウン/キーマップイベントとひもづけるオブジェクトです。推奨は stage です。
* @param useKeyControl キーコントロールを使用するか指定します。
* @param useMouseWheelControl マウスホイールコントロールを使用するか指定します。
*/
public function OrbitCameraController(
targetObject:Camera3D,
followTarget:Object3D,
mouseDownEventSource:InteractiveObject,
mouseUpEventSource:InteractiveObject,
keyEventSource:InteractiveObject,
useKeyControl:Boolean = true,
useMouseWheelControl:Boolean = true, mouseWheelHandler:Function=null
)
{
_target = targetObject;
_followTarget = followTarget;
super(mouseDownEventSource, targetObject, 0, 3, mouseSensitivity);
super.mouseSensitivity = 0;
super.unbindAll();
super.accelerate(true);
this._mouseDownEventSource = mouseDownEventSource;
this._mouseUpEventSource = mouseUpEventSource;
this._keyEventSource = keyEventSource;
_mouseDownEventSource.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
_mouseUpEventSource.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
// マウスホイール操作
_mouseWheelHandler = mouseWheelHandler;
if (useMouseWheelControl)
{
_mouseDownEventSource.addEventListener(MouseEvent.MOUSE_WHEEL, (_mouseWheelHandler=mouseWheelHandler || this.mouseWheelHandler));
}
// キーボード操作
if (useKeyControl)
{
_keyEventSource.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
_keyEventSource.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
}
}
public function reset():void {
_angleLongitude = 0;
_lastLongitude = 0;
_angleLatitude = 0;
// _mouseMove = false;
if (useHandCursor)
Mouse.cursor = MouseCursor.AUTO;
}
//----------------------------------------------------------
//
// Property
//
//----------------------------------------------------------
//--------------------------------------
// easingSeparator
//--------------------------------------
private var _easingSeparator:Number = 1.5;
/**
* イージング時の現在の位置から最後の位置までの分割係数
* フレームレートと相談して使用
* 1〜
* 正の整数のみ。0 を指定しても 1 になります。
* 1 でイージング無し。数値が高いほど、遅延しぬるぬるします
*/
public function set easingSeparator(value:uint):void
{
if (value)
{
_easingSeparator = value;
}
else
{
_easingSeparator = 1;
}
}
/** Camera から、中心までの最大距離です。デフォルト値は 2000 です。 */
public var maxDistance:Number = 2000;
/** Camera から、中心までの最小距離です。デフォルト値は 200 です。 */
public var minDistance:Number = 200;
//--------------------------------------
// mouseSensitivityX
//--------------------------------------
private var _mouseSensitivityX:Number = -1;
/**
* マウスの X 方向の感度
*/
public function set mouseSensitivityX(value:Number):void
{
_mouseSensitivityX = value;
}
//--------------------------------------
// mouseSensitivityY
//--------------------------------------
private var _mouseSensitivityY:Number = 1;
/**
* マウスの Y 方向の感度
*/
public function set mouseSensitivityY(value:Number):void
{
_mouseSensitivityY = value;
}
public var multiplyValue:Number = 10;
//--------------------------------------
// needsRendering
//--------------------------------------
private var _needsRendering:Boolean;
/**
* レンダリングが必要かどうかを取得します。
* この値は update() メソッドが呼び出されたタイミングで更新されます。
*
* @see GeoCameraController.update
*/
public function get needsRendering():Boolean
{
return _needsRendering;
}
//--------------------------------------
// pitchSpeed
//--------------------------------------
private var _pitchSpeed:Number = 5;
/**
* 上下スピード
* 初期値は5(px)
*/
public function set pitchSpeed(value:Number):void
{
_pitchSpeed = value
}
public var useHandCursor:Boolean = true;
//--------------------------------------
// yawSpeed
//--------------------------------------
private var _yawSpeed:Number = 5;
/**
* 回転スピード
* 初期値は5(度)
*/
public function set yawSpeed(value:Number):void
{
_yawSpeed = value * Math.PI / 180;
}
/**
* Zoomスピード
* 初期値は5(px)
*/
public function set zoomSpeed(value:Number):void
{
_distanceSpeed = value;
}
public function get lockRotationZ():Boolean
{
return _lockRotationZ;
}
public function set lockRotationZ(value:Boolean):void
{
_lockRotationZ = value;
}
public function get followTarget():Object3D
{
return _followTarget;
}
public function get angleLatitude():Number
{
return _angleLatitude;
}
public function set angleLatitude(value:Number):void
{
_angleLatitude = value;
_lastLatitude = value;
}
public function get angleLongitude():Number
{
return _angleLongitude;
}
public function set angleLongitude(value:Number):void
{
_angleLongitude = value;
_lastLongitude = value;
}
public function get minAngleLatitude():Number
{
return _minAngleLatitude;
}
public function set minAngleLatitude(value:Number):void
{
_minAngleLatitude = value;
}
public function get maxAngleLatidude():Number
{
return _maxAngleLatidude;
}
public function set maxAngleLatidude(value:Number):void
{
_maxAngleLatidude = value;
}
private var _minAngleLatitude:Number = -Number.MAX_VALUE;
private var _maxAngleLatidude:Number = Number.MAX_VALUE;
public var _angleLatitude:Number = 0;
public var _angleLongitude:Number = 0;
private var _distanceSpeed:Number = 5;
private var _keyEventSource:InteractiveObject;
private var _lastLatitude:Number = 0;
private var _lastLength:Number = 700;
private var _lastLongitude:Number = _angleLongitude;
private var _lastLookAtX:Number = _lookAtX;
private var _lastLookAtY:Number = _lookAtY;
private var _lastLookAtZ:Number = _lookAtZ;
private var _length:Number = 700;
private var _lookAtX:Number = 0;
private var _lookAtY:Number = 0;
private var _lookAtZ:Number = 0;
private var _mouseDownEventSource:InteractiveObject;
private var _mouseMove:Boolean;
private var _mouseUpEventSource:InteractiveObject;
private var _mouseX:Number;
private var _mouseY:Number;
private var _oldLatitude:Number;
private var _oldLongitude:Number;
private var _pitchDown:Boolean;
private var _pitchUp:Boolean;
private var _taregetDistanceValue:Number = 0;
public var _taregetPitchValue:Number = 0;
private var _taregetYawValue:Number = 0;
public var _target:Camera3D
private var _yawLeft:Boolean;
private var _yawRight:Boolean;
private var _zoomIn:Boolean;
private var _zoomOut:Boolean;
public var _followTarget:Object3D;
//----------------------------------------------------------
//
// Function
//
//----------------------------------------------------------
/**
* 自動的に適切なキーを割り当てます。
*/
public function bindBasicKey():void
{
bindKey(Keyboard.LEFT, SimpleObjectController.ACTION_YAW_LEFT);
bindKey(Keyboard.RIGHT, SimpleObjectController.ACTION_YAW_RIGHT);
bindKey(Keyboard.DOWN, SimpleObjectController.ACTION_PITCH_DOWN);
bindKey(Keyboard.UP, SimpleObjectController.ACTION_PITCH_UP);
bindKey(Keyboard.PAGE_UP, OrbitCameraController.ACTION_BACKWARD);
bindKey(Keyboard.PAGE_DOWN, OrbitCameraController.ACTION_FORWARD);
}
/** 上方向に移動します。 */
public function pitchUp():void
{
_taregetPitchValue = _pitchSpeed * multiplyValue;
}
/** 下方向に移動します。 */
public function pitchDown():void
{
_taregetPitchValue = _pitchSpeed * -multiplyValue;
}
/** 左方向に移動します。 */
public function yawLeft():void
{
_taregetYawValue = _yawSpeed * multiplyValue;
}
/** 右方向に移動します。 */
public function yawRight():void
{
_taregetYawValue = _yawSpeed * -multiplyValue;
}
/** 中心へ向かって近づきます。 */
public function moveForeward():void
{
_taregetDistanceValue -= _distanceSpeed * multiplyValue;
}
/** 中心から遠くに離れます。 */
public function moveBackward():void
{
_taregetDistanceValue += _distanceSpeed * multiplyValue;
}
/**
* @inheritDoc
*/
override public function bindKey(keyCode:uint, action:String):void
{
switch (action)
{
case ACTION_FORWARD:
keyBindings[keyCode] = toggleForward;
break
case ACTION_BACKWARD:
keyBindings[keyCode] = toggleBackward;
break
case ACTION_YAW_LEFT:
keyBindings[keyCode] = toggleYawLeft;
break
case ACTION_YAW_RIGHT:
keyBindings[keyCode] = toggleYawRight;
break
case ACTION_PITCH_DOWN:
keyBindings[keyCode] = togglePitchDown;
break
case ACTION_PITCH_UP:
keyBindings[keyCode] = togglePitchUp;
break
}
//super.bindKey(keyCode, action)
}
/**
* 下方向に移動し続けるかを設定します。
* @param value true の場合は移動が有効になります。
*/
public function togglePitchDown(value:Boolean):void
{
_pitchDown = value
}
/**
* 上方向に移動し続けるかを設定します。
* @param value true の場合は移動が有効になります。
*/
public function togglePitchUp(value:Boolean):void
{
_pitchUp = value
}
/**
* 左方向に移動し続けるかを設定します。
* @param value true の場合は移動が有効になります。
*/
public function toggleYawLeft(value:Boolean):void
{
_yawLeft = value;
}
/**
* 右方向に移動し続けるかを設定します。
* @param value true の場合は移動が有効になります。
*/
public function toggleYawRight(value:Boolean):void
{
_yawRight = value;
}
/**
* 中心方向に移動し続けるかを設定します。
* @param value true の場合は移動が有効になります。
*/
public function toggleForward(value:Boolean):void
{
_zoomIn = value;
}
/**
* 中心と反対方向に移動し続けるかを設定します。
* @param value true の場合は移動が有効になります。
*/
public function toggleBackward(value:Boolean):void
{
_zoomOut = value;
}
private var testLook:Vector3D = new Vector3D();
/**
* @inheritDoc
*/
override public function update():void
{
const RADIAN:Number = Math.PI / 180;
var oldAngleLatitude:Number = _angleLatitude;
var oldAngleLongitude:Number = _angleLongitude;
var oldLengh:Number = _lastLength;
//CameraZoom制御
if (_zoomIn)
{
_lastLength -= _distanceSpeed;
}
else if (_zoomOut)
{
_lastLength += _distanceSpeed;
}
// ズーム制御
if (_taregetDistanceValue != 0)
{
_lastLength += _taregetDistanceValue;
_taregetDistanceValue = 0;
}
if (_lastLength < minDistance)
{
_lastLength = minDistance;
}
else if (_lastLength > maxDistance)
{
_lastLength = maxDistance;
}
if (_lastLength - _length)
{
_length += (_lastLength - _length) / _easingSeparator;
}
if (Math.abs(_lastLength - _length) < ROUND_VALUE)
{
_length = _lastLength;
}
//Camera回転制御
if (_mouseMove)
{
_lastLongitude = _oldLongitude + (_mouseDownEventSource.mouseX - _mouseX) * _mouseSensitivityX
}
else if (_yawLeft)
{
_lastLongitude += _yawSpeed;
}
else if (_yawRight)
{
_lastLongitude -= _yawSpeed;
}
if (_taregetYawValue)
{
_lastLongitude += _taregetYawValue;
_taregetYawValue = 0;
}
if (_lastLongitude - _angleLongitude)
{
_angleLongitude += (_lastLongitude - _angleLongitude) / _easingSeparator;
}
if (Math.abs(_lastLatitude - _angleLatitude) < ROUND_VALUE)
{
_angleLatitude = _lastLatitude;
}
//CameraZ位置制御
if (_mouseMove)
{
_lastLatitude = _oldLatitude + (_mouseDownEventSource.mouseY - _mouseY) * _mouseSensitivityY;
}
else if (_pitchDown)
{
_lastLatitude -= _pitchSpeed;
}
else if (_pitchUp)
{
_lastLatitude += _pitchSpeed;
}
if (_taregetPitchValue)
{
_lastLatitude += _taregetPitchValue;
_taregetPitchValue = 0;
}
_lastLatitude = Math.max(-89.9, Math.min(_lastLatitude, 89.9));
if (_lastLatitude - _angleLatitude)
{
_angleLatitude += (_lastLatitude - _angleLatitude) / _easingSeparator;
}
if (Math.abs(_lastLongitude - _angleLongitude) < ROUND_VALUE)
{
_angleLongitude = _lastLongitude;
}
if (_angleLatitude < _minAngleLatitude) _angleLatitude = _minAngleLatitude;
if (_angleLatitude > _maxAngleLatidude) _angleLatitude = _maxAngleLatidude;
var vec3d:Vector3D = this.translateGeoCoords(_angleLatitude, _angleLongitude, _length);
testLook.x = _followTarget.x;
testLook.y = _followTarget.y;
testLook.z = _followTarget.z;
// testLook = _followTarget.localToGlobal(testLook);
_target.x = testLook.x + vec3d.x;
_target.y = testLook.y + vec3d.y;
_target.z = testLook.z + vec3d.z;
//lookAt制御
if (_lastLookAtX - _lookAtX)
{
_lookAtX += (_lastLookAtX - _lookAtX) / _easingSeparator;
}
if (_lastLookAtY - _lookAtY)
{
_lookAtY += (_lastLookAtY - _lookAtY) / _easingSeparator;
}
if (_lastLookAtZ - _lookAtZ)
{
_lookAtZ += (_lastLookAtZ - _lookAtZ) / _easingSeparator;
}
//super.update()
updateObjectTransform();
lookAtXYZ(_lookAtX + testLook.x, _lookAtY + testLook.y, _lookAtZ + testLook.z);
_needsRendering = oldAngleLatitude != _angleLatitude
|| oldAngleLongitude != _angleLongitude
|| oldLengh != _length;
}
/** @inheritDoc */
override public function startMouseLook():void
{
// 封印
}
/** @inheritDoc */
override public function stopMouseLook():void
{
// 封印
}
/**
* Cameraの向く方向を指定します。
* lookAtやlookAtXYZとの併用は不可。
* @param x
* @param y
* @param z
* @param immediate trueで、イージングしないで変更
*/
public function lookAtPosition(x:Number, y:Number, z:Number, immediate:Boolean = false):void
{
if (immediate)
{
_lookAtX = x
_lookAtY = y
_lookAtZ = z
}
_lastLookAtX = x
_lastLookAtY = y
_lastLookAtZ = z
}
/**
* 経度を設定します。
* @param value 0で、正面から中央方向(lookAtPosition)を見る
* @param immediate trueで、イージングしないで変更
*/
public function setLongitude(value:Number, immediate:Boolean = false):void
{
if (immediate)
{
_angleLongitude = value;
}
_lastLongitude = value;
}
/**
* 緯度を設定します。
* @param value 0で、正面から中央方向(lookAtPosition)を見る
* @param immediate trueで、イージングしないで変更
*/
public function setLatitude(value:Number, immediate:Boolean = false):void
{
if (immediate)
{
_angleLatitude = value;
}
_lastLatitude = value;
}
/**
* Cameraから、targetObjectまでの距離を設定します。
* @param value Cameraから、targetObjectまでの距離
* @param immediate trueで、イージングしないで変更
*/
public function setDistance(value:Number, immediate:Boolean = false):void
{
if (immediate)
{
_length = value;
}
_lastLength = value;
}
public function getDistance():Number {
return _length;
}
/**
* オブジェクトを使用不可にしてメモリを解放します。
*/
public function dispose():void
{
// イベントの解放
if (_mouseDownEventSource)
_mouseDownEventSource.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
// マウスホイール操作
if (_mouseDownEventSource)
_mouseDownEventSource.removeEventListener(MouseEvent.MOUSE_WHEEL, _mouseWheelHandler);
// キーボード操作
if (_keyEventSource)
{
_keyEventSource.removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
_keyEventSource.removeEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
}
// プロパティーの解放
_easingSeparator = 0;
maxDistance = 0;
minDistance = 0;
_mouseSensitivityX = 0;
_mouseSensitivityY = 0;
multiplyValue = 0;
_needsRendering = false;
_pitchSpeed = 0;
useHandCursor = false;
_yawSpeed = 0;
_angleLatitude = 0;
_angleLongitude = 0;
_distanceSpeed = 0;
_keyEventSource = null;
_lastLatitude = 0;
_lastLength = 0;
_lastLongitude = 0;
_lastLookAtX = 0;
_lastLookAtY = 0;
_lastLookAtZ = 0;
_length = 0;
_lookAtX = 0;
_lookAtY = 0;
_lookAtZ = 0;
_mouseDownEventSource = null;
_mouseMove = false;
_mouseUpEventSource = null;
_mouseX = 0;
_mouseY = 0;
_oldLatitude = 0;
_oldLongitude = 0;
_pitchDown = false;
_pitchUp = false;
_taregetDistanceValue = 0;
_taregetPitchValue = 0;
_taregetYawValue = 0;
_target = null;
_yawLeft = false;
_yawRight = false;
_zoomIn = false;
_zoomOut = false;
}
protected function mouseDownHandler(event:Event):void
{
_oldLongitude = _lastLongitude;
_oldLatitude = _lastLatitude;
_mouseX = _mouseDownEventSource.mouseX;
_mouseY = _mouseDownEventSource.mouseY;
_mouseMove = true;
if (useHandCursor)
Mouse.cursor = MouseCursor.HAND;
}
protected function mouseUpHandler(event:Event):void
{
if (!_mouseMove) return;
if (useHandCursor)
Mouse.cursor = MouseCursor.AUTO;
_mouseMove = false;
}
private function keyDownHandler(event:KeyboardEvent):void
{
for (var key:String in keyBindings)
{
if (String(event.keyCode) == key)
{
keyBindings[key](true)
}
}
}
private function keyUpHandler(event:KeyboardEvent = null):void
{
for (var key:String in keyBindings)
{
keyBindings[key](false)
}
}
private function mouseWheelHandler(event:MouseEvent):void
{
_lastLength -= event.delta * 20;
if (_lastLength < minDistance)
{
_lastLength = minDistance
}
else if (_lastLength > maxDistance)
{
_lastLength = maxDistance
}
}
/**
* 経度と緯度から位置を算出します。
* @param latitude 緯度
* @param longitude 経度
* @param radius 半径
* @return 位置情報
*/
private function translateGeoCoords(latitude:Number, longitude:Number, radius:Number):Vector3D
{
const latitudeDegreeOffset:Number = 90;
const longitudeDegreeOffset:Number = -90;
latitude = Math.PI * latitude / 180;
longitude = Math.PI * longitude / 180;
latitude -= (latitudeDegreeOffset * (Math.PI / 180));
longitude -= (longitudeDegreeOffset * (Math.PI / 180));
var x:Number = radius * Math.sin(latitude) * Math.cos(longitude);
var y:Number = radius * Math.cos(latitude);
var z:Number = radius * Math.sin(latitude) * Math.sin(longitude);
return new Vector3D(x, z, y);
}
}
//}