Files
alternativa3d-archive/Alternativa3D5/5.0.0/alternativa/engine3d/core/Camera3D.as
2024-10-05 12:11:16 +01:00

892 lines
31 KiB
ActionScript
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 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-сцены на экране.
*/
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;
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;
}
}
/**
* Угол поля зрения в радианах в режиме перспективной проекции.
*/
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;
}
}
}