package alternativa.engine3d.core { import alternativa.engine3d.*; import alternativa.engine3d.display.Skin; import alternativa.engine3d.display.View; import alternativa.engine3d.materials.DrawPoint; import alternativa.engine3d.materials.SurfaceMaterial; import alternativa.types.Matrix3D; import alternativa.types.Point3D; import alternativa.types.Set; import flash.geom.Matrix; import flash.geom.Point; import alternativa.utils.MathUtils; use namespace alternativa3d; /** * Камера для отображения 3D-сцены на экране. * *
Направление камеры совпадает с её локальной осью Z, поэтому только что созданная камера смотрит вверх в системе * координат родителя. * *
Для отображения видимой через камеру части сцены на экран, к камере должна быть подключёна область вывода —
* экземпляр класса alternativa.engine3d.display.View.
*
* @see alternativa.engine3d.display.View
*/
public class Camera3D extends Object3D {
/**
* @private
* Расчёт матрицы пространства камеры
*/
alternativa3d var calculateMatrixOperation:Operation = new Operation("calculateMatrix", this, calculateMatrix, Operation.CAMERA_CALCULATE_MATRIX);
/**
* @private
* Расчёт плоскостей отсечения
*/
alternativa3d var calculatePlanesOperation:Operation = new Operation("calculatePlanes", this, calculatePlanes, Operation.CAMERA_CALCULATE_PLANES);
/**
* @private
* Отрисовка
*/
alternativa3d var renderOperation:Operation = new Operation("render", this, render, Operation.CAMERA_RENDER);
// Инкремент количества объектов
private static var counter:uint = 0;
/**
* @private
* Поле зрения
*/
alternativa3d var _fov:Number = Math.PI/2;
/**
* @private
* Фокусное расстояние
*/
alternativa3d var focalLength:Number;
/**
* @private
* Перспективное искажение
*/
alternativa3d var focalDistortion:Number;
/**
* @private
* Флаги рассчитанности UV-матриц
*/
alternativa3d var uvMatricesCalculated:Set = new Set(true);
// Всмомогательные точки для расчёта UV-матриц
private var textureA:Point3D = new Point3D();
private var textureB:Point3D = new Point3D();
private var textureC:Point3D = new Point3D();
/**
* @private
* Вид из камеры
*/
alternativa3d var _view:View;
/**
* @private
* Режим отрисовки
*/
alternativa3d var _orthographic:Boolean = false;
private var fullDraw:Boolean;
// Масштаб
private var _zoom:Number = 1;
// Синус половинчатого угла обзора камеры
private var viewAngle:Number;
// Направление камеры
private var direction:Point3D = new Point3D(0, 0, 1);
// Обратная трансформация камеры
private var cameraMatrix:Matrix3D = new Matrix3D();
// Скины
private var firstSkin:Skin;
private var prevSkin:Skin;
private var currentSkin:Skin;
// Плоскости отсечения
private var leftPlane:Point3D = new Point3D();
private var rightPlane:Point3D = new Point3D();
private var topPlane:Point3D = new Point3D();
private var bottomPlane:Point3D = new Point3D();
private var leftOffset:Number;
private var rightOffset:Number;
private var topOffset:Number;
private var bottomOffset:Number;
// Вспомогательные массивы точек для отрисовки
private var points1:Array = new Array();
private var points2:Array = new Array();
/**
* Создание нового экземпляра камеры.
*
* @param name имя экземпляра
*/
public function Camera3D(name:String = null) {
super(name);
}
/**
* @private
*/
private function calculateMatrix():void {
// Расчёт матрицы пространства камеры
cameraMatrix.copy(transformation);
cameraMatrix.invert();
if (_orthographic) {
cameraMatrix.scale(_zoom, _zoom, _zoom);
}
// Направление камеры
direction.x = transformation.c;
direction.y = transformation.g;
direction.z = transformation.k;
direction.normalize();
}
/**
* @private
* Расчёт плоскостей отсечения
*/
private function calculatePlanes():void {
var halfWidth:Number = _view._width*0.5;
var halfHeight:Number = _view._height*0.5;
var aw:Number = transformation.a*halfWidth;
var ew:Number = transformation.e*halfWidth;
var iw:Number = transformation.i*halfWidth;
var bh:Number = transformation.b*halfHeight;
var fh:Number = transformation.f*halfHeight;
var jh:Number = transformation.j*halfHeight;
if (_orthographic) {
// Расчёт плоскостей отсечения в изометрии
aw /= _zoom;
ew /= _zoom;
iw /= _zoom;
bh /= _zoom;
fh /= _zoom;
jh /= _zoom;
// Левая плоскость
leftPlane.x = transformation.f*transformation.k - transformation.j*transformation.g;
leftPlane.y = transformation.j*transformation.c - transformation.b*transformation.k;
leftPlane.z = transformation.b*transformation.g - transformation.f*transformation.c;
leftOffset = (transformation.d - aw)*leftPlane.x + (transformation.h - ew)*leftPlane.y + (transformation.l - iw)*leftPlane.z;
// Правая плоскость
rightPlane.x = -leftPlane.x;
rightPlane.y = -leftPlane.y;
rightPlane.z = -leftPlane.z;
rightOffset = (transformation.d + aw)*rightPlane.x + (transformation.h + ew)*rightPlane.y + (transformation.l + iw)*rightPlane.z;
// Верхняя плоскость
topPlane.x = transformation.g*transformation.i - transformation.k*transformation.e;
topPlane.y = transformation.k*transformation.a - transformation.c*transformation.i;
topPlane.z = transformation.c*transformation.e - transformation.g*transformation.a;
topOffset = (transformation.d - bh)*topPlane.x + (transformation.h - fh)*topPlane.y + (transformation.l - jh)*topPlane.z;
// Нижняя плоскость
bottomPlane.x = -topPlane.x;
bottomPlane.y = -topPlane.y;
bottomPlane.z = -topPlane.z;
bottomOffset = (transformation.d + bh)*bottomPlane.x + (transformation.h + fh)*bottomPlane.y + (transformation.l + jh)*bottomPlane.z;
} else {
// Вычисляем расстояние фокуса
focalLength = Math.sqrt(_view._width*_view._width + _view._height*_view._height)*0.5/Math.tan(0.5*_fov);
// Вычисляем минимальное (однопиксельное) искажение перспективной коррекции
focalDistortion = 1/(focalLength*focalLength);
// Расчёт плоскостей отсечения в перспективе
var cl:Number = transformation.c*focalLength;
var gl:Number = transformation.g*focalLength;
var kl:Number = transformation.k*focalLength;
// Угловые вектора пирамиды видимости
var leftTopX:Number = -aw - bh + cl;
var leftTopY:Number = -ew - fh + gl;
var leftTopZ:Number = -iw - jh + kl;
var rightTopX:Number = aw - bh + cl;
var rightTopY:Number = ew - fh + gl;
var rightTopZ:Number = iw - jh + kl;
var leftBottomX:Number = -aw + bh + cl;
var leftBottomY:Number = -ew + fh + gl;
var leftBottomZ:Number = -iw + jh + kl;
var rightBottomX:Number = aw + bh + cl;
var rightBottomY:Number = ew + fh + gl;
var rightBottomZ:Number = iw + jh + kl;
// Левая плоскость
leftPlane.x = leftBottomY*leftTopZ - leftBottomZ*leftTopY;
leftPlane.y = leftBottomZ*leftTopX - leftBottomX*leftTopZ;
leftPlane.z = leftBottomX*leftTopY - leftBottomY*leftTopX;
leftOffset = transformation.d*leftPlane.x + transformation.h*leftPlane.y + transformation.l*leftPlane.z;
// Правая плоскость
rightPlane.x = rightTopY*rightBottomZ - rightTopZ*rightBottomY;
rightPlane.y = rightTopZ*rightBottomX - rightTopX*rightBottomZ;
rightPlane.z = rightTopX*rightBottomY - rightTopY*rightBottomX;
rightOffset = transformation.d*rightPlane.x + transformation.h*rightPlane.y + transformation.l*rightPlane.z;
// Верхняя плоскость
topPlane.x = leftTopY*rightTopZ - leftTopZ*rightTopY;
topPlane.y = leftTopZ*rightTopX - leftTopX*rightTopZ;
topPlane.z = leftTopX*rightTopY - leftTopY*rightTopX;
topOffset = transformation.d*topPlane.x + transformation.h*topPlane.y + transformation.l*topPlane.z;
// Нижняя плоскость
bottomPlane.x = rightBottomY*leftBottomZ - rightBottomZ*leftBottomY;
bottomPlane.y = rightBottomZ*leftBottomX - rightBottomX*leftBottomZ;
bottomPlane.z = rightBottomX*leftBottomY - rightBottomY*leftBottomX;
bottomOffset = transformation.d*bottomPlane.x + transformation.h*bottomPlane.y + transformation.l*bottomPlane.z;
// Расчёт угла конуса
var length:Number = Math.sqrt(leftTopX*leftTopX + leftTopY*leftTopY + leftTopZ*leftTopZ);
leftTopX /= length;
leftTopY /= length;
leftTopZ /= length;
length = Math.sqrt(rightTopX*rightTopX + rightTopY*rightTopY + rightTopZ*rightTopZ);
rightTopX /= length;
rightTopY /= length;
rightTopZ /= length;
length = Math.sqrt(leftBottomX*leftBottomX + leftBottomY*leftBottomY + leftBottomZ*leftBottomZ);
leftBottomX /= length;
leftBottomY /= length;
leftBottomZ /= length;
length = Math.sqrt(rightBottomX*rightBottomX + rightBottomY*rightBottomY + rightBottomZ*rightBottomZ);
rightBottomX /= length;
rightBottomY /= length;
rightBottomZ /= length;
viewAngle = leftTopX*direction.x + leftTopY*direction.y + leftTopZ*direction.z;
var dot:Number = rightTopX*direction.x + rightTopY*direction.y + rightTopZ*direction.z;
viewAngle = (dot < viewAngle) ? dot : viewAngle;
dot = leftBottomX*direction.x + leftBottomY*direction.y + leftBottomZ*direction.z;
viewAngle = (dot < viewAngle) ? dot : viewAngle;
dot = rightBottomX*direction.x + rightBottomY*direction.y + rightBottomZ*direction.z;
viewAngle = (dot < viewAngle) ? dot : viewAngle;
viewAngle = Math.sin(Math.acos(viewAngle));
}
}
/**
* @private
*/
private function render():void {
// Режим отрисовки
fullDraw = (calculateMatrixOperation.queued || calculatePlanesOperation.queued);
// Очистка рассчитанных текстурных матриц
uvMatricesCalculated.clear();
// Отрисовка
prevSkin = null;
currentSkin = firstSkin;
renderBSPNode(_scene.bsp);
// Удаление ненужных скинов
while (currentSkin != null) {
removeCurrentSkin();
}
}
/**
* @private
*/
private function renderBSPNode(node:BSPNode):void {
if (node != null) {
var primitive:*;
var normal:Point3D = node.normal;
var cameraAngle:Number = direction.x*normal.x + direction.y*normal.y + direction.z*normal.z;
var cameraOffset:Number;
if (!_orthographic) {
cameraOffset = globalCoords.x*normal.x + globalCoords.y*normal.y + globalCoords.z*normal.z - node.offset;
}
if (node.primitive != null) {
// В ноде только базовый примитив
if (_orthographic ? (cameraAngle < 0) : (cameraOffset > 0)) {
// Камера спереди ноды
if (_orthographic || cameraAngle < viewAngle) {
renderBSPNode(node.back);
drawSkin(node.primitive);
}
renderBSPNode(node.front);
} else {
// Камера сзади ноды
if (_orthographic || cameraAngle > -viewAngle) {
renderBSPNode(node.front);
}
renderBSPNode(node.back);
}
} else {
// В ноде несколько примитивов
if (_orthographic ? (cameraAngle < 0) : (cameraOffset > 0)) {
// Камера спереди ноды
if (_orthographic || cameraAngle < viewAngle) {
renderBSPNode(node.back);
for (primitive in node.frontPrimitives) {
drawSkin(primitive);
}
}
renderBSPNode(node.front);
} else {
// Камера сзади ноды
if (_orthographic || cameraAngle > -viewAngle) {
renderBSPNode(node.front);
for (primitive in node.backPrimitives) {
drawSkin(primitive);
}
}
renderBSPNode(node.back);
}
}
}
}
/**
* @private
* Отрисовка скина примитива
*/
private function drawSkin(primitive:PolyPrimitive):void {
if (!fullDraw && currentSkin != null && currentSkin.primitive == primitive && !_scene.changedPrimitives[primitive]) {
// Пропуск скина
prevSkin = currentSkin;
currentSkin = currentSkin.nextSkin;
} else {
// Проверка поверхности
var surface:Surface = primitive.face._surface;
if (surface == null) {
return;
}
// Проверка материала
var material:SurfaceMaterial = surface._material;
if (material == null || !material.canDraw(primitive)) {
return;
}
// Отсечение выходящих за окно просмотра частей
var i:uint;
var length:uint = primitive.num;
var primitivePoint:Point3D;
var primitiveUV:Point;
var point:DrawPoint;
var useUV:Boolean = !_orthographic && material.useUV && primitive.face.uvMatrixBase;
if (useUV) {
// Формируем список точек и UV-координат полигона
for (i = 0; i < length; i++) {
primitivePoint = primitive.points[i];
primitiveUV = primitive.uvs[i];
point = points1[i];
if (point == null) {
points1[i] = new DrawPoint(primitivePoint.x, primitivePoint.y, primitivePoint.z, primitiveUV.x, primitiveUV.y);
} else {
point.x = primitivePoint.x;
point.y = primitivePoint.y;
point.z = primitivePoint.z;
point.u = primitiveUV.x;
point.v = primitiveUV.y;
}
}
} else {
// Формируем список точек полигона
for (i = 0; i < length; i++) {
primitivePoint = primitive.points[i];
point = points1[i];
if (point == null) {
points1[i] = new DrawPoint(primitivePoint.x, primitivePoint.y, primitivePoint.z);
} else {
point.x = primitivePoint.x;
point.y = primitivePoint.y;
point.z = primitivePoint.z;
}
}
}
// Отсечение по левой стороне
length = clip(length, points1, points2, leftPlane, leftOffset, useUV);
if (length < 3) {
return;
}
// Отсечение по правой стороне
length = clip(length, points2, points1, rightPlane, rightOffset, useUV);
if (length < 3) {
return;
}
// Отсечение по верхней стороне
length = clip(length, points1, points2, topPlane, topOffset, useUV);
if (length < 3) {
return;
}
// Отсечение по нижней стороне
length = clip(length, points2, points1, bottomPlane, bottomOffset, useUV);
if (length < 3) {
return;
}
if (fullDraw || _scene.changedPrimitives[primitive]) {
// Если конец списка скинов
if (currentSkin == null) {
// Добавляем скин в конец
addCurrentSkin();
} else {
if (fullDraw || _scene.changedPrimitives[currentSkin.primitive]) {
// Очистка скина
currentSkin.material.clear(currentSkin);
} else {
// Вставка скина перед текущим
insertCurrentSkin();
}
}
// Переводим координаты в систему камеры
var x:Number;
var y:Number;
var z:Number;
for (i = 0; i < length; i++) {
point = points1[i];
x = point.x;
y = point.y;
z = point.z;
point.x = cameraMatrix.a*x + cameraMatrix.b*y + cameraMatrix.c*z + cameraMatrix.d;
point.y = cameraMatrix.e*x + cameraMatrix.f*y + cameraMatrix.g*z + cameraMatrix.h;
point.z = cameraMatrix.i*x + cameraMatrix.j*y + cameraMatrix.k*z + cameraMatrix.l;
}
// Назначаем скину примитив и материал
currentSkin.primitive = primitive;
currentSkin.material = material;
material.draw(this, currentSkin, length, points1);
// Переключаемся на следующий скин
prevSkin = currentSkin;
currentSkin = currentSkin.nextSkin;
} else {
// Удаление ненужных скинов
while (currentSkin != null && _scene.changedPrimitives[currentSkin.primitive]) {
removeCurrentSkin();
}
// Переключение на следующий скин
if (currentSkin != null) {
prevSkin = currentSkin;
currentSkin = currentSkin.nextSkin;
}
}
}
}
/**
* @private
* Отсечение полигона плоскостью.
*/
private function clip(length:uint, points1:Array, points2:Array, plane:Point3D, offset:Number, calculateUV:Boolean):uint {
var i:uint;
var k:Number;
var index:uint = 0;
var point:DrawPoint;
var point1:DrawPoint;
var point2:DrawPoint;
var offset1:Number;
var offset2:Number;
point1 = points1[length - 1];
offset1 = plane.x*point1.x + plane.y*point1.y + plane.z*point1.z - offset;
if (calculateUV) {
for (i = 0; i < length; i++) {
point2 = points1[i];
offset2 = plane.x*point2.x + plane.y*point2.y + plane.z*point2.z - offset;
if (offset2 > 0) {
if (offset1 <= 0) {
k = offset2/(offset2 - offset1);
point = points2[index];
if (point == null) {
point = new DrawPoint(point2.x - (point2.x - point1.x)*k, point2.y - (point2.y - point1.y)*k, point2.z - (point2.z - point1.z)*k, point2.u - (point2.u - point1.u)*k, point2.v - (point2.v - point1.v)*k);
points2[index] = point;
} else {
point.x = point2.x - (point2.x - point1.x)*k;
point.y = point2.y - (point2.y - point1.y)*k;
point.z = point2.z - (point2.z - point1.z)*k;
point.u = point2.u - (point2.u - point1.u)*k;
point.v = point2.v - (point2.v - point1.v)*k;
}
index++;
}
point = points2[index];
if (point == null) {
point = new DrawPoint(point2.x, point2.y, point2.z, point2.u, point2.v);
points2[index] = point;
} else {
point.x = point2.x;
point.y = point2.y;
point.z = point2.z;
point.u = point2.u;
point.v = point2.v;
}
index++;
} else {
if (offset1 > 0) {
k = offset2/(offset2 - offset1);
point = points2[index];
if (point == null) {
point = new DrawPoint(point2.x - (point2.x - point1.x)*k, point2.y - (point2.y - point1.y)*k, point2.z - (point2.z - point1.z)*k, point2.u - (point2.u - point1.u)*k, point2.v - (point2.v - point1.v)*k);
points2[index] = point;
} else {
point.x = point2.x - (point2.x - point1.x)*k;
point.y = point2.y - (point2.y - point1.y)*k;
point.z = point2.z - (point2.z - point1.z)*k;
point.u = point2.u - (point2.u - point1.u)*k;
point.v = point2.v - (point2.v - point1.v)*k;
}
index++;
}
}
offset1 = offset2;
point1 = point2;
}
} else {
for (i = 0; i < length; i++) {
point2 = points1[i];
offset2 = plane.x*point2.x + plane.y*point2.y + plane.z*point2.z - offset;
if (offset2 > 0) {
if (offset1 <= 0) {
k = offset2/(offset2 - offset1);
point = points2[index];
if (point == null) {
point = new DrawPoint(point2.x - (point2.x - point1.x)*k, point2.y - (point2.y - point1.y)*k, point2.z - (point2.z - point1.z)*k);
points2[index] = point;
} else {
point.x = point2.x - (point2.x - point1.x)*k;
point.y = point2.y - (point2.y - point1.y)*k;
point.z = point2.z - (point2.z - point1.z)*k;
}
index++;
}
point = points2[index];
if (point == null) {
point = new DrawPoint(point2.x, point2.y, point2.z);
points2[index] = point;
} else {
point.x = point2.x;
point.y = point2.y;
point.z = point2.z;
}
index++;
} else {
if (offset1 > 0) {
k = offset2/(offset2 - offset1);
point = points2[index];
if (point == null) {
point = new DrawPoint(point2.x - (point2.x - point1.x)*k, point2.y - (point2.y - point1.y)*k, point2.z - (point2.z - point1.z)*k);
points2[index] = point;
} else {
point.x = point2.x - (point2.x - point1.x)*k;
point.y = point2.y - (point2.y - point1.y)*k;
point.z = point2.z - (point2.z - point1.z)*k;
}
index++;
}
}
offset1 = offset2;
point1 = point2;
}
}
return index;
}
/**
* @private
* Добавление текущего скина.
*/
private function addCurrentSkin():void {
currentSkin = Skin.createSkin();
_view.canvas.addChild(currentSkin);
if (prevSkin == null) {
firstSkin = currentSkin;
} else {
prevSkin.nextSkin = currentSkin;
}
}
/**
* @private
* Вставляем под текущий скин.
*/
private function insertCurrentSkin():void {
var skin:Skin = Skin.createSkin();
_view.canvas.addChildAt(skin, _view.canvas.getChildIndex(currentSkin));
skin.nextSkin = currentSkin;
if (prevSkin == null) {
firstSkin = skin;
} else {
prevSkin.nextSkin = skin;
}
currentSkin = skin;
}
/**
* @private
* Удаляет текущий скин.
*/
private function removeCurrentSkin():void {
// Сохраняем следующий
var next:Skin = currentSkin.nextSkin;
// Удаляем из канваса
_view.canvas.removeChild(currentSkin);
// Очистка скина
if (currentSkin.material != null) {
currentSkin.material.clear(currentSkin);
}
// Зачищаем ссылки
currentSkin.nextSkin = null;
currentSkin.primitive = null;
currentSkin.material = null;
// Удаляем
Skin.destroySkin(currentSkin);
// Следующий устанавливаем текущим
currentSkin = next;
// Устанавливаем связь от предыдущего скина
if (prevSkin == null) {
firstSkin = currentSkin;
} else {
prevSkin.nextSkin = currentSkin;
}
}
/**
* @private
*/
alternativa3d function calculateUVMatrix(face:Face, width:uint, height:uint):void {
// Расчёт точек базового примитива в координатах камеры
var point:Point3D = face.primitive.points[0];
textureA.x = cameraMatrix.a*point.x + cameraMatrix.b*point.y + cameraMatrix.c*point.z;
textureA.y = cameraMatrix.e*point.x + cameraMatrix.f*point.y + cameraMatrix.g*point.z;
point = face.primitive.points[1];
textureB.x = cameraMatrix.a*point.x + cameraMatrix.b*point.y + cameraMatrix.c*point.z;
textureB.y = cameraMatrix.e*point.x + cameraMatrix.f*point.y + cameraMatrix.g*point.z;
point = face.primitive.points[2];
textureC.x = cameraMatrix.a*point.x + cameraMatrix.b*point.y + cameraMatrix.c*point.z;
textureC.y = cameraMatrix.e*point.x + cameraMatrix.f*point.y + cameraMatrix.g*point.z;
// Находим AB и AC
var abx:Number = textureB.x - textureA.x;
var aby:Number = textureB.y - textureA.y;
var acx:Number = textureC.x - textureA.x;
var acy:Number = textureC.y - textureA.y;
// Расчёт текстурной матрицы
var uvMatrixBase:Matrix = face.uvMatrixBase;
var uvMatrix:Matrix = face.uvMatrix;
uvMatrix.a = (uvMatrixBase.a*abx + uvMatrixBase.b*acx)/width;
uvMatrix.b = (uvMatrixBase.a*aby + uvMatrixBase.b*acy)/width;
uvMatrix.c = -(uvMatrixBase.c*abx + uvMatrixBase.d*acx)/height;
uvMatrix.d = -(uvMatrixBase.c*aby + uvMatrixBase.d*acy)/height;
uvMatrix.tx = (uvMatrixBase.tx + uvMatrixBase.c)*abx + (uvMatrixBase.ty + uvMatrixBase.d)*acx + textureA.x + cameraMatrix.d;
uvMatrix.ty = (uvMatrixBase.tx + uvMatrixBase.c)*aby + (uvMatrixBase.ty + uvMatrixBase.d)*acy + textureA.y + cameraMatrix.h;
// Помечаем, как рассчитанную
uvMatricesCalculated[face] = true;
}
/**
* Поле вывода, в котором происходит отрисовка камеры.
*/
public function get view():View {
return _view;
}
/**
* @private
*/
public function set view(value:View):void {
if (value != _view) {
if (_view != null) {
_view.camera = null;
}
if (value != null) {
value.camera = this;
}
}
}
/**
* Включение режима аксонометрической проекции.
*
* @default false
*/
public function get orthographic():Boolean {
return _orthographic;
}
/**
* @private
*/
public function set orthographic(value:Boolean):void {
if (_orthographic != value) {
// Отправляем сигнал об изменении типа камеры
addOperationToScene(calculateMatrixOperation);
// Сохраняем новое значение
_orthographic = value;
}
}
/**
* Угол поля зрения в радианах в режиме перспективной проекции. При изменении FOV изменяется фокусное расстояние
* камеры по формуле f = d/tan(fov/2), где d является половиной диагонали поля вывода.
* Угол зрения ограничен диапазоном 0-180 градусов.
*/
public function get fov():Number {
return _fov;
}
/**
* @private
*/
public function set fov(value:Number):void {
value = (value < 0) ? 0 : ((value > (Math.PI - 0.0001)) ? (Math.PI - 0.0001) : value);
if (_fov != value) {
// Если перспектива
if (!_orthographic) {
// Отправляем сигнал об изменении плоскостей отсечения
addOperationToScene(calculatePlanesOperation);
}
// Сохраняем новое значение
_fov = value;
}
}
/**
* Коэффициент увеличения изображения в режиме аксонометрической проекции.
*/
public function get zoom():Number {
return _zoom;
}
/**
* @private
*/
public function set zoom(value:Number):void {
value = (value < 0) ? 0 : value;
if (_zoom != value) {
// Если изометрия
if (_orthographic) {
// Отправляем сигнал об изменении zoom
addOperationToScene(calculateMatrixOperation);
}
// Сохраняем новое значение
_zoom = value;
}
}
/**
* @inheritDoc
*/
override protected function addToScene(scene:Scene3D):void {
super.addToScene(scene);
if (_view != null) {
// Отправляем операцию расчёта плоскостей отсечения
scene.addOperation(calculatePlanesOperation);
// Подписываемся на сигналы сцены
scene.changePrimitivesOperation.addSequel(renderOperation);
}
}
/**
* @inheritDoc
*/
override protected function removeFromScene(scene:Scene3D):void {
super.removeFromScene(scene);
// Удаляем все операции из очереди
scene.removeOperation(calculateMatrixOperation);
scene.removeOperation(calculatePlanesOperation);
scene.removeOperation(renderOperation);
if (_view != null) {
// Отписываемся от сигналов сцены
scene.changePrimitivesOperation.removeSequel(renderOperation);
}
}
/**
* @private
*/
alternativa3d function addToView(view:View):void {
// Сохраняем первый скин
firstSkin = (view.canvas.numChildren > 0) ? Skin(view.canvas.getChildAt(0)) : null;
// Подписка на свои операции
// При изменении камеры пересчёт матрицы
calculateTransformationOperation.addSequel(calculateMatrixOperation);
// При изменении матрицы или FOV пересчёт плоскостей отсечения
calculateMatrixOperation.addSequel(calculatePlanesOperation);
// При изменении плоскостей перерисовка
calculatePlanesOperation.addSequel(renderOperation);
if (_scene != null) {
// Отправляем сигнал перерисовки
_scene.addOperation(calculateMatrixOperation);
// Подписываемся на сигналы сцены
_scene.changePrimitivesOperation.addSequel(renderOperation);
}
// Сохраняем вид
_view = view;
}
/**
* @private
*/
alternativa3d function removeFromView(view:View):void {
// Сброс ссылки на первый скин
firstSkin = null;
// Отписка от своих операций
// При изменении камеры пересчёт матрицы
calculateTransformationOperation.removeSequel(calculateMatrixOperation);
// При изменении матрицы или FOV пересчёт плоскостей отсечения
calculateMatrixOperation.removeSequel(calculatePlanesOperation);
// При изменении плоскостей перерисовка
calculatePlanesOperation.removeSequel(renderOperation);
if (_scene != null) {
// Удаляем все операции из очереди
_scene.removeOperation(calculateMatrixOperation);
_scene.removeOperation(calculatePlanesOperation);
_scene.removeOperation(renderOperation);
// Отписываемся от сигналов сцены
_scene.changePrimitivesOperation.removeSequel(renderOperation);
}
// Удаляем ссылку на вид
_view = null;
}
/**
* @inheritDoc
*/
override protected function defaultName():String {
return "camera" + ++counter;
}
/**
* @inheritDoc
*/
protected override function createEmptyObject():Object3D {
return new Camera3D();
}
/**
* @inheritDoc
*/
protected override function clonePropertiesFrom(source:Object3D):void {
super.clonePropertiesFrom(source);
var src:Camera3D = Camera3D(source);
orthographic = src._orthographic;
zoom = src._zoom;
fov = src._fov;
}
}
}