mirror of
https://github.com/MapMakersAndProgrammers/alternativa3d-archive.git
synced 2025-10-27 18:29:07 -07:00
892 lines
31 KiB
ActionScript
892 lines
31 KiB
ActionScript
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;
|
||
}
|
||
}
|
||
} |