Files
alternativa3d-archive/Alternativa3D2/2.0/com/alternativagame/engine3d/.svn/text-base/View3D.as.svn-base
2024-10-05 12:11:16 +01:00

541 lines
17 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.alternativagame.engine3d {
import com.alternativagame.engine3d.object.Object3D;
import com.alternativagame.engine3d.skin.Skin;
import com.alternativagame.type.RGB;
import com.alternativagame.type.Set;
import com.alternativagame.type.Vector;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.ColorTransform;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.clearTimeout;
import flash.utils.setTimeout;
use namespace engine3d;
public class View3D extends Sprite {
use namespace engine3d;
// Корневой объект
private var _object:Object3D = null;
// Область отрисовки спрайтов
private var canvas:Sprite;
private var canvasCoords:Vector;
// Список скинов на изменение глубины
private var skinsToDepth:Array;
// Список скинов на перепозиционирование
private var skinsToPosition:Set;
// Список скинов на отрисовку
private var skinsToDraw:Set;
// Список скинов на освещение
private var skinsToLight:Set;
// Размеры окна камеры
private var _width:uint;
private var _height:uint;
// Флаг ограничения окна камеры
private var _crop:Boolean = false;
// Координаты камеры относительно начала координат
private var _targetX:Number = 0;
private var _targetY:Number = 0;
private var _targetZ:Number = 0;
// Повороты камеры
private var _pitch:Number = 0;
private var _roll:Number = 0;
private var _yaw:Number = 0;
// Степень увеличения объектов
private var _zoom:Number = 1;
// Трансформация камеры
engine3d var transformation:Matrix3D;
engine3d var inverseTransformation:Matrix3D;
// Изменилась точка обзора камеры
engine3d var positionChanged:Boolean = true;
// Изменился угол обзора или масштаб
engine3d var geometryChanged:Boolean = true;
// Флаг заморозки камеры
private var _hold:Boolean = false;
// Текущий нажатый объект
private var pressedObject:Object3D;
public function View3D(width:uint, height:uint) {
hitArea = new Sprite();
hitArea.mouseEnabled = false;
hitArea.visible = false;
with (hitArea.graphics) {
beginFill(0);
drawRect(0, 0, 100, 100);
}
addChild(hitArea);
canvas = new Sprite();
canvas.mouseEnabled = false;
canvas.mouseChildren = false;
addChild(canvas);
canvasCoords = new Vector();
this.width = width;
this.height = height;
skinsToDepth = new Array();
skinsToPosition = new Set();
skinsToDraw = new Set();
skinsToLight = new Set();
addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
private function onMouseDown(e:MouseEvent):void {
dispatchEvent3D(Event3D.DOWN, e.ctrlKey, e.altKey, e.shiftKey);
}
private function onMouseUp(e:MouseEvent):void {
dispatchEvent3D(Event3D.UP, e.ctrlKey, e.altKey, e.shiftKey);
}
private function dispatchEvent3D(type:String, ctrlKey:Boolean, altKey:Boolean, shiftKey:Boolean):void {
var mouse:Point = new Point(stage.mouseX, stage.mouseY);
var skin:Skin = getSkinFromPoint(mouse);
// Если нажали на интерактивный скин
if (skin != null && skin.interactive) {
// При нажатии сохраняем нажатый объект
if (type == Event3D.DOWN) {
pressedObject = skin.object;
}
var click:Boolean = (type == Event3D.UP && pressedObject == skin.object);
// Получаем пересечение вектора мыши со скином
var canvasCoords:Vector = skin.getIntersectionCoords(canvas.globalToLocal(mouse));
// Формируем ветку объектов
var objectList:Array = skin.object.getBranch();
// Перевести точку в мировые координаты
var worldCoords:Vector = Math3D.vectorTransform(canvasCoords, inverseTransformation);
// Рассчитываем точку в координатах каждого из родительских объектах и формируем список
var coordsList:Array = new Array();
var objectMatrix:Matrix3D;
var objectCoords:Vector = worldCoords.clone();
var currentObject:Object3D;
// Перебираем список объектов с конца (с корневого объекта)
var i:int;
for (i = objectList.length - 1; i >= 0; i--) {
currentObject = objectList[i];
// Трансформируем точку через матрицу в локальные координаты текущего объекта
objectCoords = Math3D.vectorTransform(objectCoords, currentObject.inverseTransform);
coordsList[i] = objectCoords.clone();
}
// Рассылаем события от объектов
for (i = 0; i < objectList.length; i++) {
currentObject = objectList[i];
currentObject.dispatchEvent(new Event3D(type, ctrlKey, altKey, shiftKey, skin.object, skin.polygon, skin.material, canvasCoords, objectCoords, coordsList[i]));
// Если отжали на нажатом объекте, то отправить ещё и клик
if (click) {
currentObject.dispatchEvent(new Event3D(Event3D.CLICK, ctrlKey, altKey, shiftKey, skin.object, skin.polygon, skin.material, canvasCoords, objectCoords, coordsList[i]));
}
}
// Отослать событие от камеры
dispatchEvent(new Event3D(type, ctrlKey, altKey, shiftKey, skin.object, skin.polygon, skin.material, canvasCoords, objectCoords, worldCoords));
// Если отжали на нажатом объекте, то отправить ещё и клик
if (click) {
dispatchEvent(new Event3D(Event3D.CLICK, ctrlKey, altKey, shiftKey, skin.object, skin.polygon, skin.material, canvasCoords, objectCoords, worldCoords));
}
} else {
// При нажатии на пустое место сбрасываем нажатый объект
if (type == Event3D.DOWN) {
pressedObject = null;
}
// Рассылаем пустое событие
dispatchEvent(new Event3D(type, ctrlKey, altKey, shiftKey));
// Если нажатый объект также был пуст, то отправить клик на пустое место
if (type == Event3D.UP && pressedObject == null) {
dispatchEvent(new Event3D(Event3D.CLICK, ctrlKey, altKey, shiftKey));
}
}
}
// Получить скин по заданным координатам
public function getSkinFromPoint(point:Point):Skin {
// Получаем список объектов под координатой
var objectList:Array = getObjectsUnderPoint(point);
// Оставить в списке только скины
var skinList:Array = new Array();
var len:uint = objectList.length;
for (var i:uint = 0; i < len; i++) {
if (objectList[i] is Skin) {
skinList.push(objectList[i]);
}
}
// Сортируем их по глубине
skinList.sortOn("sortDepth", Array.NUMERIC);
// Возвращаем самый близкий
return skinList[0];
}
// Заморозить изображение камеры
public function hold():void {
_hold = true;
canvas.cacheAsBitmap = true;
}
// Заморозить изображение камеры
public function unhold():void {
_hold = false;
canvas.cacheAsBitmap = false;
}
// Перерисовать объекты в камере
public function draw():void {
if (object != null) {
// Если изменилась геометрия
if (geometryChanged) {
// Пересчитать трансформацию
transformation = new Matrix3D();
Math3D.rotateZMatrix(transformation, -_yaw);
Math3D.rotateYMatrix(transformation, -_roll);
Math3D.rotateXMatrix(transformation, -_pitch);
Math3D.scaleMatrix(transformation, _zoom, _zoom, _zoom);
}
// Если изменилась позиция
if (geometryChanged || positionChanged) {
// Передвигаем всю область скинов
canvasCoords = Math3D.vectorTransform(new Vector(-_targetX, -_targetY, -_targetZ), transformation);
// Пересчитать инверсную трансформацию
var inv:Matrix3D = new Matrix3D(_targetX, _targetY, _targetZ, _pitch, _roll, _yaw, 1/_zoom, 1/_zoom, 1/_zoom);
inv.d += inv.a*canvasCoords.x + inv.b*canvasCoords.y + inv.c*canvasCoords.z;
inv.h += inv.e*canvasCoords.x + inv.f*canvasCoords.y + inv.g*canvasCoords.z;
inv.l += inv.i*canvasCoords.x + inv.j*canvasCoords.y + inv.k*canvasCoords.z;
inverseTransformation = inv;
canvas.x = width/2 + canvasCoords.x;
canvas.y = height/2 - canvasCoords.z;
}
geometryChanged = false;
positionChanged = false;
// Если камера не заморожена
if (!_hold) {
// Расчитываем трансформацию дерева объектов
object.calculateTransform();
// Расчитать освещение дерева объектов
object.calculateLight();
if (skinsToDepth.length > 0 || skinsToPosition.length > 0 || skinsToDraw.length > 0 || skinsToLight.length > 0) {
trace(skinsToDepth.length, skinsToDraw.length, skinsToPosition.length, skinsToLight.length);
}
// Сортируем глубины
sortDepths();
var skin:Skin;
// Позиционируем скины
for each (skin in skinsToPosition) {
skin.position();
}
skinsToPosition = new Set();
// Отрисовываем скины
for each (skin in skinsToDraw) {
skin.draw();
}
skinsToDraw = new Set();
// Освещаем скины
for each (skin in skinsToLight) {
skin.light();
}
skinsToLight = new Set();
}
}
}
// Запуск стирания скина
private function clearSkin(skin:Skin, index:int, arr:Array):void {
canvas.removeChild(DisplayObject(skin));
}
// Сортировка глубин скинов
private function sortDepths():void {
// Убираем скины из списка
skinsToDepth.forEach(clearSkin);
// Сортируем скины по глубине
skinsToDepth.sortOn("sortDepth", Array.NUMERIC | Array.DESCENDING);
// Вставляем скины на нужные глубины
var ma:int = -1;
var mb:int = (canvas.numChildren > 0) ? canvas.numChildren : 0;
var side:Boolean = false;
var skin:Skin;
var a:int;
var b:int;
var c:int;
var len:uint = skinsToDepth.length;
for (var i:uint = 0; i < len; i++) {
skin = (side) ? skinsToDepth.pop() : skinsToDepth.shift();
a = ma;
b = mb;
while (a < b - 1) {
c = (a + b) >>> 1;
(skin.sortDepth >= (canvas.getChildAt(c) as Skin).sortDepth) ? (b = c) : (a = c);
}
canvas.addChildAt(DisplayObject(skin), b);
(side) ? (mb = b) : (ma = b);
mb++;
side = !side;
}
}
// Добавить скин в список изменения глубин
engine3d function addToDepth(skin:Skin):void {
if (skinsToDepth.indexOf(skin) < 0) skinsToDepth.push(skin);
}
// Добавить скин в список репозиционированных в следующий раз
engine3d function addToPosition(skin:Skin):void {
skinsToPosition.add(skin);
}
// Добавить скин в список отрисовываемых в следующий раз
engine3d function addToDraw(skin:Skin):void {
skinsToDraw.add(skin);
}
// Добавить скин в список освещаемых в следующий раз
engine3d function addToLight(skin:Skin):void {
skinsToLight.add(skin);
}
// Добавить скин
engine3d function addSkin(skin:Skin):void {
canvas.addChild(DisplayObject(skin));
}
// Убрать скин
engine3d function removeSkin(skin:Skin):void {
// Удаляем скин из камеры
canvas.removeChild(DisplayObject(skin));
// Удаляем из списка на сортировку
var i:int = skinsToDepth.indexOf(skin);
if (i>=0) skinsToDepth.splice(i,1);
// Удаляем из список на отрисовку, позиционирование и освещение
skinsToPosition.remove(skin);
skinsToDraw.remove(skin);
skinsToLight.remove(skin);
}
// Указать корневой объект
public function set object(value:Object3D):void {
// Если есть текущий объект
if (object != null) {
// Снимаем у него камеру
object.setView(null);
}
// Если устанавливаем не пустой объект
if (value != null) {
// Если объект был в другой камере и был там корневым
if (value.view != null && value === value.view.object) {
// Снимаем у той камеры объект
value.view.object = null;
} else {
// Если объект был в другом объекте
if (value.parent != null) {
// Удалить его оттуда
value.parent.detach(value);
}
}
// Указываем объектам камеру
value.setView(this);
}
_object = value;
}
public function get object():Object3D {
return _object;
}
public function get targetX():Number {
return _targetX;
}
public function get targetY():Number {
return _targetY;
}
public function get targetZ():Number {
return _targetZ;
}
public function get zoom():Number {
return _zoom;
}
public function get pitch():Number {
return _pitch;
}
public function get roll():Number {
return _roll;
}
public function get yaw():Number {
return _yaw;
}
public function set targetX(value:Number):void {
_targetX = value;
positionChanged = true;
}
public function set targetY(value:Number):void {
_targetY = value;
positionChanged = true;
}
public function set targetZ(value:Number):void {
_targetZ = value;
positionChanged = true;
}
public function set pitch(value:Number):void {
_pitch = value;
setGeometryChanged();
}
public function set roll(value:Number):void {
_roll = value;
setGeometryChanged();
}
public function set yaw(value:Number):void {
_yaw = value;
setGeometryChanged();
}
public function set zoom(value:Number):void {
_zoom = value;
setGeometryChanged();
}
engine3d function setGeometryChanged():void {
// Изменить геометрию у объекта и его потомков
if (!geometryChanged && object != null) {
object.setGeometryChanged();
geometryChanged = true;
}
}
override public function set width(value:Number):void {
_width = value;
hitArea.width = _width;
canvas.x = _width/2 + canvasCoords.x;
if (crop) {
scrollRect = new Rectangle(0, 0, _width, height);
}
}
override public function get width():Number {
return _width;
}
override public function set height(value:Number):void {
_height = value;
hitArea.height = _height;
canvas.y = _height/2 - canvasCoords.z;
if (crop) {
scrollRect = new Rectangle(0, 0, width, _height);
}
}
override public function get height():Number {
return _height;
}
public function set crop(value:Boolean):void {
_crop = value;
if (value) {
scrollRect = new Rectangle(0, 0, width, height);
} else {
scrollRect = null;
}
}
public function get crop():Boolean {
return _crop;
}
public function get mouseCanvasCoords():Point {
var res:Point = null;
if (stage != null) {
res = canvas.globalToLocal(new Point(stage.mouseX, stage.mouseY));
}
return res;
}
public function canvasToView(coords:Vector):Vector {
return Math3D.vectorAdd(coords, canvasCoords);
}
public function viewToCanvas(coords:Vector):Vector {
return Math3D.vectorSub(coords, canvasCoords);
}
}
}