This commit is contained in:
Pyogenics
2024-09-28 17:40:49 +01:00
parent c2b65211bd
commit ecf60d961b
49 changed files with 6864 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
package alternativa {
/**
* Класс содержит информацию о версии библиотеки.
* Также используется для интеграции библиотеки в среду разработки Adobe Flash.
*/
public class Alternativa3D {
/**
* Версия библиотеки в формате: версия.подверсия.сборка
*/
public static const version:String = "6.0.0";
}
}

View File

@@ -0,0 +1,3 @@
package alternativa.engine3d {
public namespace alternativa3d = "http://alternativaplatform.com/en/alternativa3d";
}

View File

@@ -0,0 +1,564 @@
package alternativa.engine3d.core {
import alternativa.engine3d.*;
import alternativa.engine3d.display.Canvas;
import alternativa.engine3d.display.DisplayItem;
import alternativa.engine3d.display.Skin;
import alternativa.engine3d.display.View;
import alternativa.engine3d.sorting.Node;
import alternativa.types.Matrix3D;
import alternativa.types.Set;
import alternativa.utils.MathUtils;
import flash.display.DisplayObjectContainer;
import flash.utils.Dictionary;
use namespace alternativa3d;
public class Camera3D extends Object3D {
// Инкремент количества объектов
private static var counter:uint = 0;
/**
* @private
* Поле зрения
*/
alternativa3d var _fov:Number = MathUtils.DEG90;
/**
* @private
* Фокусное расстояние
*/
alternativa3d var focalLength:Number;
/**
* @private
* Перспективное искажение
*/
alternativa3d var focalDistortion:Number;
/**
* @private
* Вид из камеры
*/
alternativa3d var _view:View;
//Половина ширины вьюпорта
private var halfWidth:Number;
//Половина высоты вьюпорта
private var halfHeight:Number;
/**
* @private
* Режим отрисовки
*/
alternativa3d var _orthographic:Boolean = false;
/**
* @private
* Матрица перевода из системы координат камеры в глобальную
*/
alternativa3d var globalMatrix:Matrix3D = new Matrix3D();
/**
* @private
* Матрица перевода из глобальной системы координат в систему камеры
*/
alternativa3d var inverseGlobalMatrix:Matrix3D = new Matrix3D();
// Масштаб
alternativa3d var _zoom:Number = 1;
// Реестр отрисованных канвасов
private var canvases:Dictionary = new Dictionary();
// Список канвасов на удаление
private var canvasesToRemove:Set = new Set();
/**
* Создание экземпляра камеры.
*
* @param name имя экземпляра
*/
public function Camera3D(name:String = null) {
super(name);
}
override protected function transform():void {
super.transform();
// Если есть вьюпорт
if (_view != null) {
// Помечаем камеру на глобальную трансформацию
_scene.camerasToRender[this] = true;
}
}
override protected function move():void {
super.move();
// Если есть вьюпорт
if (_view != null) {
// Помечаем камеру на глобальную трансформацию
_scene.camerasToRender[this] = true;
}
}
alternativa3d function render():void {
trace(this, "render");
// Если у пространства сцены есть материал
if (_scene._space._material != null) {
// Расчитываем половины ширины и высоты вьюпорта
halfWidth = _view._width*0.5;
halfHeight = _view._height*0.5;
// Если перспектива
if (!_orthographic) {
// Вычисляем фокусное расстояние
focalLength = Math.sqrt(halfWidth*halfWidth + halfHeight*halfHeight)/Math.tan(0.5*_fov);
// Вычисляем минимальное (однопиксельное) искажение перспективной коррекции
focalDistortion = 1/(focalLength*focalLength);
}
// Расчитываем глобальную трансформацию
globalMatrix.copy(spaceMatrix);
globalMatrix.combine(space.globalMatrix);
// Расчитываем инверсную глобальную трансформацию
inverseGlobalMatrix.copy(globalMatrix);
inverseGlobalMatrix.invert();
// Создание канваса пространства сцены, если его ещё нет
if (_view.canvas == null) {
_view.canvas = Canvas.create();
_view.addChild(_view.canvas);
_view.canvas.x = halfWidth;
_view.canvas.y = halfHeight;
canvases[_scene._space] = _view.canvas;
}
// Отрисовка пространства сцены
renderSpace(_scene._space, _view.canvas);
} else {
// Зачищаем канвас вьюпорта
removeViewCanvas();
}
// Снимаем пометки об отрисовке
delete _scene.camerasToRender[this];
}
private function renderSpace(space:Space, canvas:Canvas):void {
trace("render space", space);
// Если пространство сцены
if (space.space == null) {
// Берём в качестве матриц пространства глобальные
space.inverseCameraMatrix.copy(globalMatrix);
space.cameraMatrix.copy(inverseGlobalMatrix);
} else {
// Считаем матрицу перевода из системы координат пространства в систему камеры
space.cameraMatrix.copy(space.globalMatrix)
space.cameraMatrix.combine(inverseGlobalMatrix);
// Считаем матрицу перевода из системы координат камеры в систему пространства
space.inverseCameraMatrix.copy(space.cameraMatrix);
space.inverseCameraMatrix.invert();
}
// Направление камеры в пространстве
space.direction.x = space.inverseCameraMatrix.c;
space.direction.y = space.inverseCameraMatrix.g;
space.direction.z = space.inverseCameraMatrix.k;
// Расчёт направления и плоскостей отсечения в пространстве
if (_orthographic) {
// Масштабируем матрицу камеры
space.cameraMatrix.scale(_zoom, _zoom, _zoom);
// Расчёт плоскостей отсечения
space.calculateOrthographicPlanes(halfWidth, halfHeight, _zoom)
} else {
// Нормализуем направление камеры
space.direction.normalize();
// Расчёт плоскостей отсечения
space.calculatePerspectivePlanes(halfWidth, halfHeight, focalLength)
}
// Отрисовка BSP-дерева
/*if (space.root != null) {
canvas.previousItem = null;
canvas.currentItem = canvas.firstItem;
renderNode(space.root, canvas);
}*/
// Удаление помеченных канвасов
for (var key:* in canvasesToRemove) {
var c:Canvas = key;
var parent:DisplayObjectContainer = c.parent;
// Если есть объект перед канвасом
if (c.previous != null) {
// Устанавливаем ему ссылку на следующий
c.previous.next = c.next;
} else {
// Устанавливаем первый объект в родительском канвасе
(parent as Canvas).firstItem = c.next;
}
// Если после канваса есть канвас
if (c.next is Canvas) {
(c.next as Canvas).previous = c.previous;
}
// Удаляем из списка отображения
parent.removeChild(c);
// Отправляем на реиспользование
Canvas.destroy(c);
}
// Удаление лишних отрисовочных объектов
if (canvas.previous != null) {
// Обрываем список
canvas.previous.next = null;
}
var item:DisplayItem = canvas.currentItem;
while (item != null) {
// Сохраняем следующий
var next:DisplayItem = item.next;
// Удаляем из канваса
canvas.removeChild(item);
// Удаляем
(item is Skin) ? Skin.destroy(item as Skin) : Canvas.destroy(item as Canvas);
// Следующий устанавливаем текущим
item = next;
}
}
private function renderNode(node:Node, container:Canvas):void {
/*
var primitives:Set;
if (node is DistanceNode) {
primitives = (node as DistanceNode).primitives;
for (var key:* in primitives) {
// Если точечный примитив грани
if (key is FaceDistancePrimitive) {
var face:Face = (key as FaceDistancePrimitive).face;
var skin:Skin;
// Пропускаем канвасы и помечаем их на удаление
while (container.currentItem is Canvas) {
// Помечаем на удаление
canvasesToRemove[container.currentItem] = true;
// Переключаемся на следующий объект
container.previousItem = container.currentItem;
container.currentItem = container.currentItem.next;
}
// Если есть текущий объект
if (container.currentItem != null) {
// Берём текущий скин
skin = container.currentItem as Skin;
// Переключаемся на следующий объект
container.previousItem = skin;
container.currentItem = skin.next;
} else {
// Создаём новый скин
skin = Skin.create();
// Вставляем скин в конец
container.addChild(skin);
// Обновление списка в текущем канвасе
if (container.previousItem != null) {
container.previousItem.next = skin;
} else {
container.firstItem = skin;
}
// Переключаемся на следующий объект
container.previousItem = skin;
}
// Отрисовка скина
// ...
skin.graphics.beginFill(0xFFFF00, 0.3);
skin.graphics.drawCircle(0, 0, Math.random()*100);
} else {
// Если примитив объекта
if (key is SpriteDistancePrimitive) {
} else {
// Если примитив пространства
var space:Space = (key as SpaceDistancePrimitive).space;
var canvas:Canvas;
// Если канвас есть в реестре
if ((canvas = canvases[space]) != null) {
// Если канвасы не совпадают
if (canvas != container.currentItem) {
// Разорвать связи с соседними объектами
// Если есть объект перед канвасом
if (canvas.previous != null) {
// Устанавливаем ему ссылку на следующий
canvas.previous.next = canvas.next;
} else {
// Устанавливаем первый объект в родительском канвасе
(canvas.parent as Canvas).firstItem = canvas.next;
}
// Если после канваса есть канвас
if (canvas.next is Canvas) {
(canvas.next as Canvas).previous = canvas.previous;
}
// Создать новые связи
// Установка связи со следующим объектом
canvas.next = container.currentItem;
// Установка связи с предыдущим объектом
canvas.previous = container.previousItem;
// Обновление списка в текущем канвасе
if (container.previousItem != null) {
container.previousItem.next = canvas;
} else {
container.firstItem = canvas;
}
// Если есть текущий объект
if (container.currentItem != null) {
// Вставляем канвас перед текущим объектом
container.addChildAt(canvas, container.getChildIndex(container.currentItem));
if (container.currentItem is Canvas) {
(container.currentItem as Canvas).previous = canvas;
}
} else {
// Вставляем канвас в конец
container.addChild(canvas);
}
// Переключаемся на следующий объект
container.previousItem = canvas;
// Удалить канвас из списка на удаление
delete canvasesToRemove[canvas];
} else {
// Переключаемся на следующий объект
container.previousItem = canvas;
container.currentItem = canvas.next;
}
} else {
// Создаём новый канвас
canvas = new Canvas();
// Сохраняем его в реестр
canvases[space] = canvas;
// Создать связи
// Установка связи со следующим объектом
canvas.next = container.currentItem;
// Установка связи с предыдущим объектом
canvas.previous = container.previousItem;
// Обновление списка в текущем канвасе
if (container.previousItem != null) {
container.previousItem.next = canvas;
} else {
container.firstItem = canvas;
}
// Если есть текущий объект
if (container.currentItem != null) {
// Вставляем канвас перед текущим объектом
container.addChildAt(canvas, container.getChildIndex(container.currentItem));
if (container.currentItem is Canvas) {
(container.currentItem as Canvas).previous = canvas;
}
} else {
// Вставляем канвас в конец
container.addChild(canvas);
}
// Переключаемся на следующий объект
container.previousItem = canvas;
}
// Отрисовка пространства в канвас
renderSpace(space, canvas);
}
}
}
} else {
}
*/
}
alternativa3d function updateSpace(space:Space):void {
trace(this, "updateSpace", space);
}
alternativa3d function updateSpaceMaterial(space:Space):void {
trace(this, "updateSpaceMaterial", space);
// Если канвас нарисован, перерисовать его
var canvas:Canvas;
if ((canvas = canvases[space]) != null) {
space._material.draw(canvas);
}
}
override protected function addToScene():void {
super.addToScene();
// Добавляем камеру в реестр сцены
if (_view != null) {
_scene.cameras[this] = true;
}
}
override protected function removeFromScene():void {
super.removeFromScene();
// Если у камеры есть вьюпорт
if (_view != null) {
// Зачищаем канвас вьюпорта
removeViewCanvas();
// Удаляем камеру из реестра сцены
delete _scene.cameras[this];
// Удаляем все пометки в сцене
delete _scene.camerasToRender[this];
}
}
private function removeViewCanvas():void {
if (_view.canvas != null) {
_view.removeChild(_view.canvas);
Canvas.destroy(_view.canvas);
_view.canvas = null;
delete canvases[_scene._space];
}
}
/**
* Поле вывода, в котором происходит отрисовка камеры.
*/
public function get view():View {
return _view;
}
/**
* @private
*/
public function set view(value:View):void {
if (value != _view) {
// Если был вьюпорт
if (_view != null) {
// Зачищаем канвас вьюпорта
removeViewCanvas();
// Удалить в нём ссылку на камеру
_view._camera = null;
}
// Если назначается вьюпорт
if (value != null) {
// Если у вьюпорта была камера
if (value._camera != null) {
// Отцепить от у неё вьюпорт
value._camera.view = null;
}
// Сохранить во вьюпорте ссылку на камеру
value._camera = this;
// Если есть сцена
if (_scene != null) {
// Добавляем камеру в реестр сцены
_scene.cameras[this] = true;
// Помечаем камеру на отрисовку
_scene.camerasToRender[this] = true;
}
} else {
if (_scene != null) {
// Удаляем камеру из реестра сцены
delete _scene.cameras[this];
}
}
// Сохраняем вьюпорт
_view = value;
}
}
/**
* Включение режима аксонометрической проекции.
*
* @default false
*/
public function get orthographic():Boolean {
return _orthographic;
}
/**
* @private
*/
public function set orthographic(value:Boolean):void {
if (_orthographic != value) {
// Отправляем сигнал об изменении типа камеры
// ...
// Сохраняем новое значение
_orthographic = value;
}
}
/**
* Угол поля зрения в радианах в режиме перспективной проекции. При изменении FOV изменяется фокусное расстояние
* камеры по формуле <code>f = d/tan(fov/2)</code>, где <code>d</code> является половиной диагонали поля вывода.
* Угол зрения ограничен диапазоном 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) {
// Отправляем сигнал об изменении плоскостей отсечения
// ...
}
// Сохраняем новое значение
_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
// ...
}
// Сохраняем новое значение
_zoom = value;
}
}
/**
* @inheritDoc
*/
override protected function defaultName():String {
return "camera" + ++counter;
}
}
}

View File

@@ -0,0 +1,483 @@
package alternativa.engine3d.core {
import alternativa.engine3d.*;
import alternativa.engine3d.sorting.FaceDistancePrimitive;
import alternativa.engine3d.sorting.FaceBSPPrimitive;
import alternativa.types.Point3D;
import alternativa.types.Set;
import alternativa.engine3d.sorting.FaceNonePrimitive;
use namespace alternativa3d;
/**
* Грань, образованная тремя или более вершинами. Грани являются составными частями полигональных объектов. Каждая грань
* содержит информацию об объекте и поверхности, которым она принадлежит. Для обеспечения возможности наложения
* текстуры на грань, первым трём её вершинам могут быть заданы UV-координаты, на основании которых расчитывается
* матрица трансформации текстуры.
*/
final public class Face {
/**
* @private
* Меш
*/
alternativa3d var _mesh:Mesh;
/**
* @private
* Поверхность
*/
alternativa3d var _surface:Surface;
/**
* @private
* Вершины грани
*/
alternativa3d var _vertices:Array = new Array();
/**
* @private
* Количество вершин
*/
alternativa3d var _verticesCount:uint;
/**
* @private
* Нормаль плоскости в пространстве
*/
alternativa3d var spacePerpendicular:Point3D = new Point3D();
/**
* @private
* Примитив грани
*/
alternativa3d var primitive:FaceNonePrimitive;
alternativa3d var pointPrimitive:FaceDistancePrimitive;
alternativa3d var polyPrimitive:FaceBSPPrimitive;
alternativa3d function calculatePerpendicular():void {
trace(this, "- calculatePerpendicular");
// Вектор AB
var vertex:Vertex = _vertices[0];
var av:Point3D = vertex.spaceCoords;
vertex = _vertices[1];
var bv:Point3D = vertex.spaceCoords;
var abx:Number = bv.x - av.x;
var aby:Number = bv.y - av.y;
var abz:Number = bv.z - av.z;
// Вектор AC
vertex = _vertices[2];
var cv:Point3D = vertex.spaceCoords;
var acx:Number = cv.x - av.x;
var acy:Number = cv.y - av.y;
var acz:Number = cv.z - av.z;
// Перпендикуляр к плоскости
spacePerpendicular.x = acz*aby - acy*abz;
spacePerpendicular.y = acx*abz - acz*abx;
spacePerpendicular.z = acy*abx - acx*aby;
//TODO: проверить на нулевой перпендикуляр
}
private function preparePointPrimitive():void {
// Расчёт центра грани
pointPrimitive.coords.x = 0;
pointPrimitive.coords.y = 0;
pointPrimitive.coords.z = 0;
for each (var vertex:Vertex in _vertices) {
pointPrimitive.coords.x += vertex.spaceCoords.x;
pointPrimitive.coords.y += vertex.spaceCoords.y;
pointPrimitive.coords.z += vertex.spaceCoords.z;
}
pointPrimitive.coords.x /= _verticesCount;
pointPrimitive.coords.y /= _verticesCount;
pointPrimitive.coords.z /= _verticesCount;
}
private function preparePolyPrimitive():void {
// Расчёт смещения плоскости
// Установка приоритета
polyPrimitive.bspLevel = _surface._bspLevel;
}
alternativa3d function createPointPrimitive():void {
trace(this, "- createPointPrimitive");
// Создание точечного примитива
pointPrimitive = FaceDistancePrimitive.create();
// Подготовка точечного примитива
preparePointPrimitive();
// Добавление примитива в уровень
_surface._sortingLevel.distancePrimitivesToAdd.push(pointPrimitive);
}
alternativa3d function createPolyPrimitive():void {
trace(this, "- createPolyPrimitive");
// Создание полигонального примитива
polyPrimitive = FaceBSPPrimitive.create();
// Подготовка полигонального примитива
preparePolyPrimitive();
// Добавление примитива в уровень
_surface._sortingLevel.bspPrimitivesToAdd.push(polyPrimitive);
}
alternativa3d function updatePointPrimitive():void {
trace(this, "- updatePointPrimitive");
// Удаляем примитив
pointPrimitive.node.removePrimitive(pointPrimitive);
// Подготовка точечного примитива
preparePointPrimitive();
// Добавление примитива в уровень
_surface._sortingLevel.distancePrimitivesToAdd.push(pointPrimitive);
}
alternativa3d function updatePolyPrimitive():void {
trace(this, "- updatePolyPrimitive");
// Удаляем примитив
polyPrimitive.node.removePrimitive(polyPrimitive);
// Подготовка полигонального примитива
preparePolyPrimitive();
// Добавление примитива в уровень
_surface._sortingLevel.bspPrimitivesToAdd.push(polyPrimitive);
}
alternativa3d function redrawPointPrimitive():void {
trace(this, "- redrawPointPrimitive");
// Помечаем примитив на перерисовку
_surface._sortingLevel.changedPrimitives[pointPrimitive] = true;
}
alternativa3d function redrawPolyPrimitive():void {
trace(this, "- redrawPolyPrimitive");
_surface._sortingLevel.changedPrimitives[polyPrimitive] = true;
}
alternativa3d function changePolyToPointPrimitive():void {
trace(this, "- changePolyToPointPrimitive");
// Удаляем примитив
polyPrimitive.node.removePrimitive(polyPrimitive);
// Смена типа примитива с полигонального на точечный
FaceBSPPrimitive.defer(polyPrimitive);
polyPrimitive = null;
pointPrimitive = FaceDistancePrimitive.create();
// Подготовка точечного примитива
preparePointPrimitive();
// Добавление примитива в уровень
_surface._sortingLevel.distancePrimitivesToAdd.push(pointPrimitive);
}
alternativa3d function changePointToPolyPrimitive():void {
trace(this, "- changePointToPolyPrimitive");
// Удаляем примитив
pointPrimitive.node.removePrimitive(pointPrimitive);
// Смена типа примитива с точечного на полигональный
FaceDistancePrimitive.defer(pointPrimitive);
pointPrimitive = null;
polyPrimitive = FaceBSPPrimitive.create();
// Подготовка полигонального примитива
preparePolyPrimitive();
// Добавление примитива в уровень
_surface._sortingLevel.bspPrimitivesToAdd.push(polyPrimitive);
}
alternativa3d function destroyPointPrimitive():void {
trace(this, "- destroyPointPrimitive");
// Удаляем примитив
pointPrimitive.node.removePrimitive(pointPrimitive);
// Удаляем примитив
FaceDistancePrimitive.defer(pointPrimitive);
pointPrimitive = null;
}
alternativa3d function destroyPolyPrimitive():void {
trace(this, "- destroyPolyPrimitive");
// Удаляем примитив
polyPrimitive.node.removePrimitive(polyPrimitive);
// Разрушаем примитив
FaceBSPPrimitive.defer(polyPrimitive);
polyPrimitive = null;
}
alternativa3d function updatePolyPrimitiveBSPLevel():void {
trace(this, "- updatePolyPrimitiveBSPLevel");
// Удаляем примитив
polyPrimitive.node.removePrimitive(polyPrimitive);
// Обновляем приоритет примитива
polyPrimitive.bspLevel = _surface._bspLevel;
// Отправляем примитив на добавление в уровень
_surface._sortingLevel.bspPrimitivesToAdd.push(polyPrimitive);
}
alternativa3d function changeSurface():void {
trace(this, "changeSurface");
// Если есть поверхность и материал
if (_surface != null && _surface._material != null) {
// Если BSP-сортировка
if (_surface._sortingMode == 2) {
// Если есть полигональный примитив
if (polyPrimitive != null) {
// Если грань помечена на трансформацию
if (_mesh._scene.facesToTransform[this]) {
// Расчитываем перпендикуляр грани
calculatePerpendicular();
// Обновление полигонального примитива
updatePolyPrimitive();
// Снимаем пометку на трансформацию
delete _mesh._scene.facesToTransform[this];
} else {
// Если изменилась мобильность
if (polyPrimitive.bspLevel != _surface._bspLevel) {
// Обновляем мобильность примитива
updatePolyPrimitiveBSPLevel();
} else {
// Отправить полигональный примитив на перерисовку
redrawPolyPrimitive();
}
}
} else {
// Если есть точечный примитив
if (pointPrimitive != null) {
// Если грань помечена на трансформацию
if (_mesh._scene.facesToTransform[this]) {
// Расчитываем перпендикуляр грани
calculatePerpendicular();
// Снимаем пометку на трансформацию
delete _mesh._scene.facesToTransform[this];
}
// Смена типа примитива с точечного на полигональный
changePointToPolyPrimitive();
} else {
// Расчитываем перпендикуляр грани
calculatePerpendicular();
// Создание полигонального примитива
createPolyPrimitive();
}
}
} else {
// Если точечная сортировка
if (_surface._sortingMode == 1) {
// Если есть точечный примитив
if (pointPrimitive != null) {
// Если грань помечена на трансформацию
if (_mesh._scene.facesToTransform[this]) {
// Расчитываем перпендикуляр грани
calculatePerpendicular();
// Обновление точечного примитива
updatePointPrimitive();
// Снимаем пометку на трансформацию
delete _mesh._scene.facesToTransform[this];
} else {
// Отправить точечный примитив на перерисовку
redrawPointPrimitive();
}
} else {
// Если есть полигональный примитив
if (polyPrimitive != null) {
// Если грань помечена на трансформацию
if (_mesh._scene.facesToTransform[this]) {
// Расчитываем перпендикуляр грани
calculatePerpendicular();
// Снимаем пометку на трансформацию
delete _mesh._scene.facesToTransform[this];
}
// Смена типа примитива с полигонального на точечный
changePolyToPointPrimitive();
} else {
// Расчитываем перпендикуляр грани
calculatePerpendicular();
// Создание точечного примитива
createPointPrimitive();
}
}
} else {
// Если нет сортировки
}
}
} else {
// Если был точечный примитив
if (pointPrimitive != null) {
// Удаляем точечный примитив
destroyPointPrimitive();
// Снимаем пометку на трансформацию
delete _mesh._scene.facesToTransform[this];
} else {
// Если был полигональный примитив
if (polyPrimitive != null) {
// Удаляем полигональный примитив
destroyPolyPrimitive();
// Снимаем пометку на трансформацию
delete _mesh._scene.facesToTransform[this];
}
}
}
// Помечаем пространство на пересчёт
_mesh._scene.spacesToCalculate[_mesh.space] = true;
// Снимаем пометку на смену поверхности
delete _mesh._scene.facesToChangeSurface[this];
}
// Вызывается только при изменении координат хотя бы одной из вершин
alternativa3d function transform():void {
trace(this, "transform");
// Расчитываем перпендикуляр
calculatePerpendicular();
if (pointPrimitive != null) {
updatePointPrimitive();
} else {
updatePolyPrimitive();
}
// Помечаем пространство на пересчёт
_mesh._scene.spacesToCalculate[_mesh.space] = true;
// Снимаем пометку на трансформацию
delete _mesh._scene.facesToTransform[this];
}
/**
* Массив вершин грани, представленных объектами класса <code>alternativa.engine3d.core.Vertex</code>.
*
* @see Vertex
*/
public function get vertices():Array {
return new Array().concat(_vertices);
}
/**
* Количество вершин грани.
*/
public function get verticesCount():uint {
return _verticesCount;
}
/**
* Полигональный объект, которому принадлежит грань.
*/
public function get mesh():Mesh {
return _mesh;
}
/**
* Поверхность, которой принадлежит грань.
*/
public function get surface():Surface {
return _surface;
}
/**
* Идентификатор грани в полигональном объекте. В случае, если грань не принадлежит ни одному объекту, идентификатор
* имеет значение <code>null</code>.
*/
public function get id():Object {
return (_mesh != null) ? _mesh.getFaceId(this) : null;
}
/**
* Нормаль в локальной системе координат.
*/
public function get normal():Point3D {
var res:Point3D = new Point3D();
var vertex:Vertex = _vertices[0];
var av:Point3D = vertex.coords;
vertex = _vertices[1];
var bv:Point3D = vertex.coords;
var abx:Number = bv.x - av.x;
var aby:Number = bv.y - av.y;
var abz:Number = bv.z - av.z;
vertex = _vertices[2];
var cv:Point3D = vertex.coords;
var acx:Number = cv.x - av.x;
var acy:Number = cv.y - av.y;
var acz:Number = cv.z - av.z;
res.x = acz*aby - acy*abz;
res.y = acx*abz - acz*abx;
res.z = acy*abx - acx*aby;
if (res.x != 0 || res.y != 0 || res.z != 0) {
var k:Number = Math.sqrt(res.x*res.x + res.y*res.y + res.z*res.z);
res.x /= k;
res.y /= k;
res.z /= k;
}
return res;
}
/**
* Множество граней, имеющих общие рёбра с текущей гранью.
*/
public function get edgeJoinedFaces():Set {
var res:Set = new Set(true);
// Перебираем точки грани
for (var i:uint = 0; i < _verticesCount; i++) {
var a:Vertex = _vertices[i];
var b:Vertex = _vertices[(i < _verticesCount - 1) ? (i + 1) : 0];
// Перебираем грани текущей точки
for (var key:* in a._faces) {
var face:Face = key;
// Если это другая грань и у неё также есть следующая точка
if (face != this && face._vertices.indexOf(b) >= 0) {
// Значит у граней общее ребро
res[face] = true;
}
}
}
return res;
}
/**
* Строковое представление грани.
*
* @return строковое представление грани
*/
public function toString():String {
var res:String = "[Face ID:" + id + ((_verticesCount > 0) ? " vertices:" : "");
for (var i:uint = 0; i < _verticesCount; i++) {
var vertex:Vertex = _vertices[i];
res += vertex.id + ((i < _verticesCount - 1) ? ", " : "");
}
res += "]";
return res;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,645 @@
package alternativa.engine3d.core {
import alternativa.engine3d.*;
import alternativa.engine3d.errors.Object3DHierarchyError;
import alternativa.engine3d.errors.Object3DNotFoundError;
import alternativa.types.Matrix3D;
import alternativa.types.Point3D;
import alternativa.types.Set;
import alternativa.utils.ObjectUtils;
use namespace alternativa3d;
/**
* Базовый класс для объектов, находящихся в сцене. Класс реализует иерархию объектов сцены, а также содержит сведения
* о трансформации объекта как единого целого.
*
* <p> Масштабирование, ориентация и положение объекта задаются в родительской системе координат. Результирующая
* локальная трансформация является композицией операций масштабирования, поворотов объекта относительно осей
* <code>X</code>, <code>Y</code>, <code>Z</code> и параллельного переноса центра объекта из начала координат.
* Операции применяются в порядке их перечисления.
*
* <p> Глобальная трансформация (в системе координат корневого объекта сцены) является композицией трансформаций
* самого объекта и всех его предков по иерархии объектов сцены.
*/
public class Object3D {
// Инкремент количества объектов
private static var counter:uint = 0;
/**
* @private
* Наименование
*/
alternativa3d var _name:String;
/**
* @private
* Сцена
*/
alternativa3d var _scene:Scene3D;
/**
* @private
* Родительский объект
*/
alternativa3d var _parent:Object3D;
/**
* @private
* Дочерние объекты
*/
alternativa3d var _children:Set = new Set();
/**
* @private
* Пространство
*/
alternativa3d var space:Space;
/**
* @private
* Координаты объекта относительно родителя
*/
alternativa3d var _coords:Point3D = new Point3D();
/**
* @private
* Поворот объекта по оси X относительно родителя. Угол измеряется в радианах.
*/
alternativa3d var _rotationX:Number = 0;
/**
* @private
* Поворот объекта по оси Y относительно родителя. Угол измеряется в радианах.
*/
alternativa3d var _rotationY:Number = 0;
/**
* @private
* Поворот объекта по оси Z относительно родителя. Угол измеряется в радианах.
*/
alternativa3d var _rotationZ:Number = 0;
/**
* @private
* Мастшаб объекта по оси X относительно родителя
*/
alternativa3d var _scaleX:Number = 1;
/**
* @private
* Мастшаб объекта по оси Y относительно родителя
*/
alternativa3d var _scaleY:Number = 1;
/**
* @private
* Мастшаб объекта по оси Z относительно родителя
*/
alternativa3d var _scaleZ:Number = 1;
/**
* @private
* Матрица перевода из локальной системы координат объекта в систему пространства
*/
alternativa3d var spaceMatrix:Matrix3D = new Matrix3D();
/**
* Создание экземпляра класса.
*
* @param name имя экземпляра
*/
public function Object3D(name:String = null) {
// Имя по-умолчанию
_name = (name != null) ? name : defaultName();
}
protected function transform():void {
trace(this, "- transform");
// Если объект был в пространстве, помечаем пространство на пересчет
//if (space != null) {
// _scene.spacesToCalculate[space] = true;
//}
// Обновляем пространство
space = (_parent is Space) ? Space(_parent) : _parent.space;
// Помечаем пространство на пересчет
//_scene.spacesToCalculate[space] = true;
// Локальная матрица трансформации
spaceMatrix.toTransform(_coords.x, _coords.y, _coords.z, _rotationX, _rotationY, _rotationZ, _scaleX, _scaleY, _scaleZ);
// Если родитель не является пространством
if (!(_parent is Space)) {
// Наследуем трансформацию у родителя
spaceMatrix.combine(_parent.spaceMatrix);
}
}
protected function move():void {
trace("- move");
// Помечаем пространство на пересчет
//_scene.spacesToCalculate[space] = true;
// Если родитель является пространством
if (_parent is Space) {
// Смещение равно локальным координатам
spaceMatrix.d = _coords.x;
spaceMatrix.h = _coords.y;
spaceMatrix.l = _coords.z;
} else {
// Расчитываем новое смещение c учётом трансформации родителя
var x:Number = _coords.x;
var y:Number = _coords.y;
var z:Number = _coords.z;
var parentTransformation:Matrix3D = _parent.spaceMatrix;
spaceMatrix.d = parentTransformation.a*x + parentTransformation.b*y + parentTransformation.c*z + parentTransformation.d;
spaceMatrix.h = parentTransformation.e*x + parentTransformation.f*y + parentTransformation.g*z + parentTransformation.h;
spaceMatrix.l = parentTransformation.i*x + parentTransformation.j*y + parentTransformation.k*z + parentTransformation.l;
}
}
alternativa3d function transformBranch():void {
trace(this, "transformBranch");
// Трансформация
transform();
// Если объект не пространство, наследуем трансформацию
if (!(this is Space)) {
// Обрабатываем дочерние объекты
for (var key:* in _children) {
var child:Object3D = key;
child.transformBranch();
}
}
// Снимаем отметки о перемещении, трансформации и мобильности
delete _scene.objectsToTransform[this];
delete _scene.objectsToMove[this];
}
alternativa3d function moveBranch():void {
trace(this, "moveBranch");
// Перемещение
move();
// Если объект не пространство, наследуем перемещение
if (!(this is Space)) {
// Обрабатываем дочерние объекты
for (var key:* in _children) {
var child:Object3D = key;
_scene.objectsToTransform[child] ? child.transformBranch() : child.moveBranch();
}
}
// Снимаем отметку о перемещении
delete _scene.objectsToMove[this];
}
/**
* Добавление дочернего объекта. Добавляемый объект удаляется из списка детей предыдущего родителя.
* Новой сценой дочернего объекта становится сцена родителя.
*
* @param child добавляемый объект
*
* @throws alternativa.engine3d.errors.Object3DHierarchyError нарушение иерархии объектов сцены
*/
public function addChild(child:Object3D):void {
// Проверка на null
if (child == null) {
throw new Object3DHierarchyError(null, this);
}
// Проверка на наличие
if (child._parent == this) {
return;
}
// Проверка на добавление к самому себе
if (child == this) {
throw new Object3DHierarchyError(this, this);
}
// Проверка на добавление родительского объекта
if (child._scene == _scene) {
// Если объект был в той же сцене, либо оба не были в сцене
var parentObject:Object3D = _parent;
while (parentObject != null) {
if (child == parentObject) {
throw new Object3DHierarchyError(child, this);
return;
}
parentObject = parentObject._parent;
}
}
// Если объект был в другом объекте
if (child._parent != null) {
// Удалить его оттуда
child._parent._children.remove(child);
} else {
// Если объект был корневым в сцене
if (child._scene != null) {
child._scene.space = null;
}
}
// Добавляем в список
_children.add(child);
// Указываем себя как родителя
child._parent = this;
// Указываем сцену
child.setScene(_scene);
}
/**
* Удаление дочернего объекта.
*
* @param child удаляемый дочерний объект
*
* @throws alternativa.engine3d.errors.Object3DNotFoundError указанный объект не содержится в списке детей текущего объекта
*/
public function removeChild(child:Object3D):void {
// Проверка на null
if (child == null) {
throw new Object3DNotFoundError(null, this);
}
// Проверка на наличие
if (child._parent != this) {
throw new Object3DNotFoundError(child, this);
}
// Убираем из списка
_children.remove(child);
// Удаляем ссылку на родителя
child._parent = null;
// Удаляем ссылку на сцену
child.setScene(null);
}
/**
* @private
* Установка новой сцены для объекта.
*
* @param value сцена
*/
alternativa3d function setScene(value:Scene3D):void {
if (_scene != value) {
// Если была сцена
if (_scene != null) {
// Удалиться из сцены
removeFromScene();
}
// Сохранить сцену
_scene = value;
// Если новая сцена
if (value != null) {
// Добавиться на сцену
addToScene();
}
// Установить эту сцену у дочерних объектов
for (var key:* in _children) {
var object:Object3D = key;
object.setScene(value);
}
} else {
// При перемещении в пределах сцены пересчёт
if (_scene != null) {
_scene.objectsToTransform[this] = true;
}
}
}
/**
* Метод вызывается при добавлении объекта на сцену. Наследники могут переопределять метод для выполнения
* специфических действий.
*/
protected function addToScene():void {
_scene.objectsToTransform[this] = true;
}
/**
* Метод вызывается при удалении объекта со сцены. Наследники могут переопределять метод для выполнения
* специфических действий.
*/
protected function removeFromScene():void {
/* // Если объект был в пространстве
if (space != null) {
// Помечаем пространство на пересчет
_scene.spacesToCalculate[space] = true;
// Удаляем ссылку на пространство
space = null;
}
*/
// Удаляем ссылку на пространство
space = null;
// Удаляем все пометки в сцене
delete _scene.objectsToTransform[this];
delete _scene.objectsToMove[this];
}
/**
* Имя объекта.
*/
public function get name():String {
return _name;
}
/**
* @private
*/
public function set name(value:String):void {
_name = value;
}
/**
* Сцена, которой принадлежит объект.
*/
public function get scene():Scene3D {
return _scene;
}
/**
* Родительский объект.
*/
public function get parent():Object3D {
return _parent;
}
/**
* Набор дочерних объектов.
*/
public function get children():Set {
return _children.clone();
}
/**
* Координата X.
*/
public function get x():Number {
return _coords.x;
}
/**
* Координата Y.
*/
public function get y():Number {
return _coords.y;
}
/**
* Координата Z.
*/
public function get z():Number {
return _coords.z;
}
/**
* @private
*/
public function set x(value:Number):void {
if (_coords.x != value) {
_coords.x = value;
if (_scene != null) {
_scene.objectsToMove[this] = true;
}
}
}
/**
* @private
*/
public function set y(value:Number):void {
if (_coords.y != value) {
_coords.y = value;
if (_scene != null) {
_scene.objectsToMove[this] = true;
}
}
}
/**
* @private
*/
public function set z(value:Number):void {
if (_coords.z != value) {
_coords.z = value;
if (_scene != null) {
_scene.objectsToMove[this] = true;
}
}
}
/**
* Координаты объекта.
*/
public function get coords():Point3D {
return _coords.clone();
}
/**
* @private
*/
public function set coords(value:Point3D):void {
if (!_coords.equals(value)) {
_coords.copy(value);
if (_scene != null) {
_scene.objectsToMove[this] = true;
}
}
}
/**
* Угол поворота вокруг оси X, заданный в радианах.
*/
public function get rotationX():Number {
return _rotationX;
}
/**
* Угол поворота вокруг оси Y, заданный в радианах.
*/
public function get rotationY():Number {
return _rotationY;
}
/**
* Угол поворота вокруг оси Z, заданный в радианах.
*/
public function get rotationZ():Number {
return _rotationZ;
}
/**
* @private
*/
public function set rotationX(value:Number):void {
if (_rotationX != value) {
_rotationX = value;
if (_scene != null) {
_scene.objectsToTransform[this] = true;
}
}
}
/**
* @private
*/
public function set rotationY(value:Number):void {
if (_rotationY != value) {
_rotationY = value;
if (_scene != null) {
_scene.objectsToTransform[this] = true;
}
}
}
/**
* @private
*/
public function set rotationZ(value:Number):void {
if (_rotationZ != value) {
_rotationZ = value;
if (_scene != null) {
_scene.objectsToTransform[this] = true;
}
}
}
/**
* Коэффициент масштабирования вдоль оси X.
*/
public function get scaleX():Number {
return _scaleX;
}
/**
* Коэффициент масштабирования вдоль оси Y.
*/
public function get scaleY():Number {
return _scaleY;
}
/**
* Коэффициент масштабирования вдоль оси Z.
*/
public function get scaleZ():Number {
return _scaleZ;
}
/**
* @private
*/
public function set scaleX(value:Number):void {
if (_scaleX != value) {
_scaleX = value;
if (_scene != null) {
_scene.objectsToTransform[this] = true;
}
}
}
/**
* @private
*/
public function set scaleY(value:Number):void {
if (_scaleY != value) {
_scaleY = value;
if (_scene != null) {
_scene.objectsToTransform[this] = true;
}
}
}
/**
* @private
*/
public function set scaleZ(value:Number):void {
if (_scaleZ != value) {
_scaleZ = value;
if (_scene != null) {
_scene.objectsToTransform[this] = true;
}
}
}
/**
* Строковое представление объекта.
*
* @return строковое представление объекта
*/
public function toString():String {
return "[" + ObjectUtils.getClassName(this) + " " + _name + "]";
}
/**
* Имя объекта по умолчанию.
*
* @return имя объекта по умолчанию
*/
protected function defaultName():String {
return "object" + ++counter;
}
/**
* Создание пустого объекта без какой-либо внутренней структуры. Например, если некоторый геометрический примитив при
* своём создании формирует набор вершин, граней и поверхностей, то этот метод не должен создавать вершины, грани и
* поверхности. Данный метод используется в методе clone() и должен быть переопределён в потомках для получения
* правильного объекта.
*
* @return новый пустой объект
*/
protected function createEmptyObject():Object3D {
return new Object3D();
}
/**
* Копирование свойств объекта-источника. Данный метод используется в методе clone() и должен быть переопределён в
* потомках для получения правильного объекта. Каждый потомок должен в переопределённом методе копировать только те
* свойства, которые добавлены к базовому классу именно в нём. Копирование унаследованных свойств выполняется
* вызовом super.clonePropertiesFrom(source).
*
* @param source объект, свойства которого копируются
*/
protected function clonePropertiesFrom(source:Object3D):void {
_name = source._name;
_coords.x = source._coords.x;
_coords.y = source._coords.y;
_coords.z = source._coords.z;
_rotationX = source._rotationX;
_rotationY = source._rotationY;
_rotationZ = source._rotationZ;
_scaleX = source._scaleX;
_scaleY = source._scaleY;
_scaleZ = source._scaleZ;
}
/**
* Клонирование объекта. Для реализации собственного клонирования наследники должны переопределять методы
* <code>createEmptyObject()</code> и <code>clonePropertiesFrom()</code>.
*
* @return клонированный экземпляр объекта
*
* @see #createEmptyObject()
* @see #clonePropertiesFrom()
*/
public function clone():Object3D {
var copy:Object3D = createEmptyObject();
copy.clonePropertiesFrom(this);
// Клонирование детей
for (var key:* in _children) {
var child:Object3D = key;
copy.addChild(child.clone());
}
return copy;
}
/**
* Получение дочернего объекта с заданным именем.
*
* @param name имя дочернего объекта
* @return любой дочерний объект с заданным именем или <code>null</code> в случае отсутствия таких объектов
*/
public function getChildByName(name:String):Object3D {
for (var key:* in _children) {
var child:Object3D = key;
if (child._name == name) {
return child;
}
}
return null;
}
}
}

View File

@@ -0,0 +1,405 @@
package alternativa.engine3d.core {
import alternativa.engine3d.*;
import alternativa.types.Set;
import alternativa.utils.ObjectUtils;
import alternativa.engine3d.sorting.SortingLevel;
use namespace alternativa3d;
/**
* Сцена является контейнером 3D-объектов, с которыми ведётся работа. Все взаимодействия объектов
* происходят в пределах одной сцены. Класс обеспечивает работу системы сигналов и реализует алгоритм построения
* BSP-дерева для содержимого сцены.
*/
public class Scene3D {
// Инкремент количества сцен
private static var counter:uint = 0;
/**
* @private
* Наименование
*/
alternativa3d var _name:String;
/**
* @private
* Пространство сцены
*/
alternativa3d var _space:Space;
/**
* @private
* Реестр камер сцены
*/
alternativa3d var cameras:Set = new Set();
/**
* @private
* Списки сигналов
*/
alternativa3d var objectsToTransform:Set = new Set();
alternativa3d var objectsToMove:Set = new Set();
// alternativa3d var verticesToMove:Set = new Set();
alternativa3d var spacesToGlobalTransform:Set = new Set();
alternativa3d var spacesToChangeSortingMode:Set = new Set();
alternativa3d var spacesToChangeSortingLevel:Set = new Set();
alternativa3d var spacesToChangeMaterial:Set = new Set();
/*
alternativa3d var spritesToChangeSortingMode:Set = new Set();
alternativa3d var spritesToChangeSortingLevel:Set = new Set();
alternativa3d var spritesToChangeMaterial:Set = new Set();
alternativa3d var surfacesToChangeSortingMode:Set = new Set();
alternativa3d var surfacesToChangeSortingLevel:Set = new Set();
alternativa3d var surfacesToChangeMaterial:Set = new Set();
alternativa3d var surfacesToChangeBSPLevel:Set = new Set();
alternativa3d var facesToChangeSurface:Set = new Set();
alternativa3d var facesToTransform:Set = new Set();
*/
//alternativa3d var spacesToCalculate:Set = new Set();
alternativa3d var levelsToCalculate:Set = new Set();
//alternativa3d var spacesToRender:Set = new Set();
alternativa3d var spacesToUpdateMaterial:Set = new Set();
alternativa3d var camerasToRender:Set = new Set();
alternativa3d var levelsToClear:Set = new Set();
/**
* Создание экземпляра сцены.
*/
public function Scene3D(name:String = null) {
// Имя по-умолчанию
_name = (name != null) ? name : defaultName();
}
/**
* Расчёт сцены. Метод анализирует все изменения, произошедшие с момента предыдущего расчёта, формирует список
* команд и исполняет их в необходимой последовательности. В результате расчёта происходит перерисовка во всех
* областях вывода, к которым подключены находящиеся в сцене камеры.
*/
public function calculate():void {
trace("----------------------------- calculate -------------------------------------");
var key:*;
var object:Object3D;
var method:Function;
// var vertex:Vertex;
// var face:Face;
// var surface:Surface;
// var sprite:Sprite3D;
var space:Space;
var level:SortingLevel;
var camera:Camera3D;
// Трансформация объектов
if (!objectsToTransform.isEmpty()) {
trace("objectsToTransform:", objectsToTransform.length);
}
while ((object = objectsToTransform.peek()) != null) {
// Ищем, нет ли выше в пространстве объектов на трансформацию или перемещение
method = object.transformBranch;
while ((object = object._parent) != null && !(object is Space)) {
method = objectsToTransform[object] ? object.transformBranch : (objectsToMove[object] ? object.moveBranch : method);
}
method();
}
// Перемещение объектов
if (!objectsToMove.isEmpty()) {
trace("objectsToMove:", objectsToMove.length);
}
while ((object = objectsToMove.peek()) != null) {
// Ищем, нет ли выше в пространстве объектов на перемещение
method = object.moveBranch;
while ((object = object._parent) != null && !(object is Space)) {
method = objectsToMove[object] ? object.moveBranch : method;
}
method();
}
/*
// Перемещение вершин
if (!verticesToMove.isEmpty()) {
trace("verticesToMove:", verticesToMove.length);
}
for (key in verticesToMove) {
vertex = key;
vertex.move();
}
*/
// Глобальная трансформация пространств
if (!spacesToGlobalTransform.isEmpty()) {
trace("spacesToGlobalTransform:", spacesToGlobalTransform.length);
}
while ((space = spacesToGlobalTransform.peek()) != null) {
method = space.globalTransform;
// Ищем, нет ли выше пространства на глобальную трансформацию
while ((space = space.space) != null) {
method = spacesToGlobalTransform[space] ? space.globalTransform : method;
}
method();
}
// Изменение режима сортировки пространств
if (!spacesToChangeSortingMode.isEmpty()) {
trace("spacesToChangeSortingMode:", spacesToChangeSortingMode.length);
}
for (key in spacesToChangeSortingMode) {
space = key;
space.changeSortingMode();
}
// Изменение уровней пространств
if (!spacesToChangeSortingLevel.isEmpty()) {
trace("spacesToChangeSortingLevel:", spacesToChangeSortingLevel.length);
}
for (key in spacesToChangeSortingLevel) {
space = key;
space.changeSortingLevel();
}
// Изменение материалов пространств
if (!spacesToChangeMaterial.isEmpty()) {
trace("spacesToChangeMaterial:", spacesToChangeMaterial.length);
}
for (key in spacesToChangeMaterial) {
space = key;
space.changeMaterial();
}
/*
// Изменение режима сортировки спрайтов
if (!spritesToChangeSortingMode.isEmpty()) {
trace("spritesToChangeSortingMode:", spritesToChangeSortingMode.length);
}
for (key in spritesToChangeSortingMode) {
sprite = key;
sprite.changeSortingMode();
}
// Изменение уровней спрайтов
if (!spritesToChangeSortingLevel.isEmpty()) {
trace("spritesToChangeSortingLevel:", spritesToChangeSortingLevel.length);
}
for (key in spritesToChangeSortingLevel) {
sprite = key;
sprite.changeSortingLevel();
}
// Изменение материалов спрайтов
if (!spritesToChangeMaterial.isEmpty()) {
trace("spritesToChangeMaterial:", spritesToChangeMaterial.length);
}
for (key in spritesToChangeMaterial) {
sprite = key;
sprite.changeMaterial();
}
// Изменение режима сортировки поверхностей
if (!surfacesToChangeSortingMode.isEmpty()) {
trace("surfacesToChangeSortingMode:", surfacesToChangeSortingMode.length);
}
for (key in surfacesToChangeSortingMode) {
surface = key;
surface.changeSortingMode();
}
// Изменение уровней поверхностей
if (!surfacesToChangeSortingLevel.isEmpty()) {
trace("surfacesToChangeSortingLevel:", surfacesToChangeSortingLevel.length);
}
for (key in surfacesToChangeSortingLevel) {
surface = key;
surface.changeSortingLevel();
}
// Изменение материалов поверхностей
if (!surfacesToChangeMaterial.isEmpty()) {
trace("surfacesToChangeMaterial:", surfacesToChangeMaterial.length);
}
for (key in surfacesToChangeMaterial) {
surface = key;
surface.changeMaterial();
}
// Изменение мобильности поверхностей
if (!surfacesToChangeBSPLevel.isEmpty()) {
trace("surfacesToChangeBSPLevel:", surfacesToChangeBSPLevel.length);
}
for (key in surfacesToChangeBSPLevel) {
surface = key;
surface.changeBSPLevel();
}
// Изменение поверхности граней
if (!facesToChangeSurface.isEmpty()) {
trace("facesToChangeSurface:", facesToChangeSurface.length);
}
for (key in facesToChangeSurface) {
face = key;
face.changeSurface();
}
// Трансформация граней
if (!facesToTransform.isEmpty()) {
trace("facesToTransform:", facesToTransform.length);
}
for (key in facesToTransform) {
face = key;
face.transform();
}
*/
// Расчёт BSP пространств
if (!levelsToCalculate.isEmpty()) {
trace("levelsToCalculate:", levelsToCalculate.length);
}
for (key in levelsToCalculate) {
level = key;
level.calculate();
}
/*
// Отрисовка пространств
if (!spacesToRender.isEmpty()) {
trace("spacesToRender:", spacesToRender.length);
}
while ((space = spacesToRender.peek()) != null) {
var s:Space = space;
// Ищем, нет ли выше пространства на отрисовку
while ((space = space.space) != null) {
s = spacesToRender[space] ? space : s;
}
// Отрисовка пространства
s.render();
}
*/
// Обновление материалов пространств
if (!spacesToUpdateMaterial.isEmpty()) {
trace("spacesToUpdateMaterial:", spacesToUpdateMaterial.length);
}
for (key in spacesToUpdateMaterial) {
space = key;
space.updateMaterial();
}
// Отрисовка камер
if (!camerasToRender.isEmpty()) {
trace("camerasToRender:", camerasToRender.length);
}
for (key in camerasToRender) {
camera = key;
camera.render();
}
// Очистка изменений уровней
if (!levelsToClear.isEmpty()) {
trace("levelsToClear:", levelsToClear.length);
}
for (key in levelsToClear) {
level = key;
level.clear();
}
// Отложенное удаление примитивов в коллекторы
//FaceNonePrimitive.destroyDeferred();
//FaceDistancePrimitive.destroyDeferred();
//FaceBSPPrimitive.destroyDeferred();
//SpaceDistancePrimitive.destroyDeferred();
//SpriteDistancePrimitive.destroyDeferred();
trace("-----------------------------------------------------------------------------");
}
public function hasChanges():Boolean {
//return !objectsToLocate.isEmpty() || !objectsToChangeMobility.isEmpty() || !objectsToTransform.isEmpty() || !objectsToMove.isEmpty() || !verticesToMove.isEmpty() || !facesToChangeSurface.isEmpty() || !facesToTransform.isEmpty();
return true;
}
/**
* Имя сцены.
*/
public function get name():String {
return _name;
}
/**
* @private
*/
public function set name(value:String):void {
_name = value;
}
/**
* Корневой объект сцены.
*/
public function get space():Space {
return _space;
}
/**
* @private
*/
public function set space(value:Space):void {
// Если ещё не является пространством сцены
if (_space != value) {
// Если у сцены было пространство
if (_space != null) {
// Удаляем у него ссылку на сцену
_space.setScene(null);
}
// Если устанавливаем пространство
if (value != null) {
// Если пространство было в другом объекте
if (value._parent != null) {
// Удалить его оттуда
value._parent._children.remove(value);
} else {
// Если пространство было пространством в другой сцене
if (value._scene != null) {
value._scene.space = null;
}
}
// Удаляем ссылку на родителя
value._parent = null;
// Указываем сцену
value.setScene(this);
// Если у пространства есть материал, помечаем его на обновление
if (value._material != null) {
spacesToUpdateMaterial[value] = true;
}
}
// Сохраняем пространство
_space = value;
}
}
/**
* Строковое представление сцены.
*
* @return строковое представление сцены
*/
public function toString():String {
return "[" + ObjectUtils.getClassName(this) + " " + _name + "]";
}
/**
* Имя сцены по умолчанию.
*
* @return имя сцены по умолчанию
*/
protected function defaultName():String {
return "scene" + ++counter;
}
}
}

View File

@@ -0,0 +1,700 @@
package alternativa.engine3d.core {
import alternativa.engine3d.*;
import alternativa.engine3d.materials.SpaceMaterial;
import alternativa.engine3d.sorting.DistanceNode;
import alternativa.engine3d.sorting.SortingLevel;
import alternativa.types.Matrix3D;
import alternativa.types.Point3D;
import alternativa.engine3d.errors.InvalidSortingModeError;
use namespace alternativa3d;
public final class Space extends Object3D {
// Инкремент количества пространств
private static var counter:uint = 0;
/**
* @private
* Режим сортировки
*/
alternativa3d var _sortingMode:uint = 1;
/**
* @private
* Материал
*/
alternativa3d var _material:SpaceMaterial;
/**
* @private
* Матрица перевода из локальной системы координат объекта в глобальную
*/
alternativa3d var globalMatrix:Matrix3D = new Matrix3D();
/**
* @private
* Первый элемент списка уровней
*/
alternativa3d var firstLevel:SortingLevel;
/**
* @private
* Номер уровня в пространстве
*/
private var sortingLevelIndex:int = 0;
/**
* @private
* Ссылка на уровень в пространстве
*/
private var _sortingLevel:SortingLevel;
/**
* @private
* Нода в которой находится спрайт
*/
alternativa3d var node:DistanceNode;
/**
* @private
* Вспомогательная матрица перевода из системы координат пространства в систему камеры
*/
alternativa3d var cameraMatrix:Matrix3D = new Matrix3D();
/**
* @private
* Вспомогательная матрица перевода из системы координат камеры в систему пространства
*/
alternativa3d var inverseCameraMatrix:Matrix3D = new Matrix3D();
// Синус половинчатого угла обзора камеры
alternativa3d var viewAngle:Number;
// Направление камеры
alternativa3d var direction:Point3D = new Point3D(0, 0, 1);
// Плоскости отсечения
alternativa3d var leftPlane:Point3D = new Point3D();
alternativa3d var rightPlane:Point3D = new Point3D();
alternativa3d var topPlane:Point3D = new Point3D();
alternativa3d var bottomPlane:Point3D = new Point3D();
alternativa3d var leftOffset:Number;
alternativa3d var rightOffset:Number;
alternativa3d var topOffset:Number;
alternativa3d var bottomOffset:Number;
public function Space(name:String = null) {
super(name);
}
override protected function transform():void {
if (_parent != null) {
super.transform();
if (_material != null) {
if (_sortingLevel != null) {
// Несортируемый
if (_sortingMode == 0) {
// Остался несортируемый
// Помечаем изменение в уровне
_sortingLevel.changed[this] = true;
_scene.levelsToClear[_sortingLevel] = true;
// Если изменился уровень
if (_sortingLevel.space != space || _sortingLevel.index != sortingLevelIndex) {
// Удаляем из старого уровня
delete _sortingLevel.spaces[this];
// Обновляем уровень
_sortingLevel = space.getSortingLevel(sortingLevelIndex);
// Добавляем в новый уровень
_sortingLevel.spaces[this] = true;
// Помечаем изменение в новом уровне
_sortingLevel.changed[this] = true;
_scene.levelsToClear[_sortingLevel] = true;
}
} else {
// Изменился на сортируемый
// Удаляем из списка несортируемых пространств уровня
delete _sortingLevel.spaces[this];
// Помечаем изменение в уровне
_sortingLevel.changed[this] = true;
_scene.levelsToClear[_sortingLevel] = true;
// Если изменился уровень
if (_sortingLevel.space != space || _sortingLevel.index != sortingLevelIndex) {
// Обновляем уровень
_sortingLevel = space.getSortingLevel(sortingLevelIndex);
}
// Добавляем в список пространств на сортировку по дистанции
_sortingLevel.spacesDistance[this] = true;
_scene.levelsToCalculate[_sortingLevel] = true;
// Удаляем ссылку на уровень
_sortingLevel = null;
}
} else {
if (node != null) {
// Сортируемый
} else {
// Нет в уровне
// Получаем уровень
_sortingLevel = space.getSortingLevel(sortingLevelIndex);
if (_sortingMode == 0) {
// Появился несортируемый
// Добавляем в уровень
_sortingLevel.spaces[this] = true;
// Помечаем изменение в уровне
_sortingLevel.changed[this] = true;
_scene.levelsToClear[_sortingLevel] = true;
} else {
// Появился сортируемый
// Добавляем в список пространств на сортировку по дистанции
_sortingLevel.spacesDistance[this] = true;
_scene.levelsToCalculate[_sortingLevel] = true;
// Удаляем ссылку на уровень
_sortingLevel = null;
}
}
}
} else {
}
/*
// Если есть примитив
if (primitive != null) {
// Если изменился уровень
if (primitive.node.sortingLevel != _sortingLevel) {
// Обновляем уровень
_sortingLevel = space.getSortingLevel(sortingLevelIndex);
}
// Удаляем примитив
primitive.node.removePrimitive(primitive);
} else {
// Обновляем уровень
_sortingLevel = space.getSortingLevel(sortingLevelIndex);
// Создаём примитив
primitive = SpaceDistancePrimitive.create();
primitive.space = this;
}
// Добавляем примитив в уровень
_sortingLevel.distancePrimitivesToAdd.push(primitive);
// Устанавливаем координаты
primitive.coords.x = spaceMatrix.d;
primitive.coords.y = spaceMatrix.h;
primitive.coords.z = spaceMatrix.l;
*/
// Помечаем пространство на глобальную трансформацию
_scene.spacesToGlobalTransform[this] = true;
} else {
// Если появился или изменился материал, помечаем на обновление материала
if (_material != null && _scene.spacesToChangeMaterial[this]) {
_scene.spacesToUpdateMaterial[this] = true;
}
}
// Снимаем пометки пространства
delete _scene.spacesToChangeSortingMode[this];
delete _scene.spacesToChangeSortingLevel[this];
delete _scene.spacesToChangeMaterial[this];
}
override protected function move():void {
super.move();
/*
// Если изменился уровень
if (_sortingLevel.index != sortingLevelIndex) {
// Обновляем уровень
_sortingLevel = space.getSortingLevel(sortingLevelIndex);
}
// Удаляем примитив
primitive.node.removePrimitive(primitive);
// Добавляем примитив в уровень
_sortingLevel.distancePrimitivesToAdd.push(primitive);
// Устанавливаем координаты
primitive.coords.x = spaceMatrix.d;
primitive.coords.y = spaceMatrix.h;
primitive.coords.z = spaceMatrix.l;
*/
// Помечаем пространство на глобальную трансформацию
_scene.spacesToGlobalTransform[this] = true;
// Снимаем пометки пространства
delete _scene.spacesToChangeSortingMode[this];
delete _scene.spacesToChangeSortingLevel[this];
delete _scene.spacesToChangeMaterial[this];
}
/*
override alternativa3d function transformBranch():void {
// Если не корневой объект
if (_parent != null) {
// Трансформируем дочернюю ветку
super.transformBranch();
} else {
trace(this, "transformBranch sceneSpace");
// Снимаем все отметки
delete _scene.objectsToTransform[this];
delete _scene.objectsToMove[this];
delete _scene.spacesToChangeSortingMode[this];
delete _scene.spacesToChangeSortingLevel[this];
delete _scene.spacesToChangeMaterial[this];
}
}
override alternativa3d function moveBranch():void {
// Если не корневой объект
if (_parent != null) {
super.moveBranch();
} else {
trace(this, "moveBranch sceneSpace");
// Снимаем отметку о перемещении
delete _scene.objectsToMove[this];
}
}
*/
alternativa3d function changeSortingMode():void {
trace(this, "changeSortingMode");
if (parent != null) {
// Снимаем пометку на смену материала
delete _scene.spacesToChangeMaterial[this];
}
// Снимаем пометки пространства
delete _scene.spacesToChangeSortingMode[this];
delete _scene.spacesToChangeSortingLevel[this];
}
alternativa3d function changeSortingLevel():void {
trace(this, "changeSortingLevel");
if (parent != null) {
/*
// Если уровень изменился
if (_sortingLevel.index != sortingLevelIndex) {
// Обновляем уровень
_sortingLevel = space.getSortingLevel(sortingLevelIndex);
// Удаляем примитив
primitive.node.removePrimitive(primitive);
// Добавляем примитив в уровень
_sortingLevel.distancePrimitivesToAdd.push(primitive);
}
*/
// Снимаем пометку на смену материала
delete _scene.spacesToChangeMaterial[this];
}
// Снимаем пометки пространства
delete _scene.spacesToChangeSortingLevel[this];
}
alternativa3d function changeMaterial():void {
trace(this, "changeMaterial");
// Если не корневое пространство
if (parent != null) {
// Снимаем пометку на смену материала
delete _scene.spacesToChangeMaterial[this];
} else {
}
}
/*
alternativa3d function render():void {
trace(this, "render");
// Обрабатываем камеры не помеченные на полную отрисовку
for (var key:* in _scene.cameras) {
if (!_scene.camerasToRender[key]) {
var camera:Camera3D = key;
camera.updateSpaceMaterial(this);
}
}
// Снимаем пометку об отрисовке пространства
delete _scene.spacesToUpdate[this];
}
*/
alternativa3d function updateMaterial():void {
trace(this, "updateMaterial");
// Обрабатываем камеры не помеченные на полную отрисовку
for (var key:* in _scene.cameras) {
if (!_scene.camerasToRender[key]) {
var camera:Camera3D = key;
camera.updateSpaceMaterial(this);
}
}
// Снимаем пометку об обновлении материала
delete _scene.spacesToUpdateMaterial[this];
}
alternativa3d function globalTransform():void {
trace(this, "globalTransform");
// Расчитываем глобальную трансформацию
globalMatrix.copy(spaceMatrix);
globalMatrix.combine(space.globalMatrix);
// Вызвать глобальную трансформацию в дочерних пространствах
globalTransformChildSpaces(this);
delete _scene.spacesToGlobalTransform[this];
}
private function globalTransformChildSpaces(object:Object3D):void {
// Обрабатываем дочерние объекты
for (var key:* in object._children) {
if (key is Space) {
(key as Space).globalTransform();
} else {
// Если камера с вьюпортом, помечаем её на отрисовку
if ((key is Camera3D) && (key as Camera3D)._view != null) {
_scene.camerasToRender[key] = true;
}
globalTransformChildSpaces(key);
}
}
}
/**
* @private
* Расчёт плоскостей отсечения для ортографической камеры
*/
alternativa3d function calculateOrthographicPlanes(halfWidth:Number, halfHeight:Number, zoom:Number):void {
var aw:Number = inverseCameraMatrix.a*halfWidth/zoom;
var ew:Number = inverseCameraMatrix.e*halfWidth/zoom;
var iw:Number = inverseCameraMatrix.i*halfWidth/zoom;
var bh:Number = inverseCameraMatrix.b*halfHeight/zoom;
var fh:Number = inverseCameraMatrix.f*halfHeight/zoom;
var jh:Number = inverseCameraMatrix.j*halfHeight/zoom;
// Левая плоскость
leftPlane.x = inverseCameraMatrix.f*inverseCameraMatrix.k - inverseCameraMatrix.j*inverseCameraMatrix.g;
leftPlane.y = inverseCameraMatrix.j*inverseCameraMatrix.c - inverseCameraMatrix.b*inverseCameraMatrix.k;
leftPlane.z = inverseCameraMatrix.b*inverseCameraMatrix.g - inverseCameraMatrix.f*inverseCameraMatrix.c;
leftOffset = (inverseCameraMatrix.d - aw)*leftPlane.x + (inverseCameraMatrix.h - ew)*leftPlane.y + (inverseCameraMatrix.l - iw)*leftPlane.z;
// Правая плоскость
rightPlane.x = -leftPlane.x;
rightPlane.y = -leftPlane.y;
rightPlane.z = -leftPlane.z;
rightOffset = (inverseCameraMatrix.d + aw)*rightPlane.x + (inverseCameraMatrix.h + ew)*rightPlane.y + (inverseCameraMatrix.l + iw)*rightPlane.z;
// Верхняя плоскость
topPlane.x = inverseCameraMatrix.g*inverseCameraMatrix.i - inverseCameraMatrix.k*inverseCameraMatrix.e;
topPlane.y = inverseCameraMatrix.k*inverseCameraMatrix.a - inverseCameraMatrix.c*inverseCameraMatrix.i;
topPlane.z = inverseCameraMatrix.c*inverseCameraMatrix.e - inverseCameraMatrix.g*inverseCameraMatrix.a;
topOffset = (inverseCameraMatrix.d - bh)*topPlane.x + (inverseCameraMatrix.h - fh)*topPlane.y + (inverseCameraMatrix.l - jh)*topPlane.z;
// Нижняя плоскость
bottomPlane.x = -topPlane.x;
bottomPlane.y = -topPlane.y;
bottomPlane.z = -topPlane.z;
bottomOffset = (inverseCameraMatrix.d + bh)*bottomPlane.x + (inverseCameraMatrix.h + fh)*bottomPlane.y + (inverseCameraMatrix.l + jh)*bottomPlane.z;
}
/**
* @private
* Расчёт плоскостей отсечения для перспективной камеры
*/
alternativa3d function calculatePerspectivePlanes(halfWidth:Number, halfHeight:Number, focalLength:Number):void {
var aw:Number = inverseCameraMatrix.a*halfWidth;
var ew:Number = inverseCameraMatrix.e*halfWidth;
var iw:Number = inverseCameraMatrix.i*halfWidth;
var bh:Number = inverseCameraMatrix.b*halfHeight;
var fh:Number = inverseCameraMatrix.f*halfHeight;
var jh:Number = inverseCameraMatrix.j*halfHeight;
var cl:Number = inverseCameraMatrix.c*focalLength;
var gl:Number = inverseCameraMatrix.g*focalLength;
var kl:Number = inverseCameraMatrix.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 = inverseCameraMatrix.d*leftPlane.x + inverseCameraMatrix.h*leftPlane.y + inverseCameraMatrix.l*leftPlane.z;
// Правая плоскость
rightPlane.x = rightTopY*rightBottomZ - rightTopZ*rightBottomY;
rightPlane.y = rightTopZ*rightBottomX - rightTopX*rightBottomZ;
rightPlane.z = rightTopX*rightBottomY - rightTopY*rightBottomX;
rightOffset = inverseCameraMatrix.d*rightPlane.x + inverseCameraMatrix.h*rightPlane.y + inverseCameraMatrix.l*rightPlane.z;
// Верхняя плоскость
topPlane.x = leftTopY*rightTopZ - leftTopZ*rightTopY;
topPlane.y = leftTopZ*rightTopX - leftTopX*rightTopZ;
topPlane.z = leftTopX*rightTopY - leftTopY*rightTopX;
topOffset = inverseCameraMatrix.d*topPlane.x + inverseCameraMatrix.h*topPlane.y + inverseCameraMatrix.l*topPlane.z;
// Нижняя плоскость
bottomPlane.x = rightBottomY*leftBottomZ - rightBottomZ*leftBottomY;
bottomPlane.y = rightBottomZ*leftBottomX - rightBottomX*leftBottomZ;
bottomPlane.z = rightBottomX*leftBottomY - rightBottomY*leftBottomX;
bottomOffset = inverseCameraMatrix.d*bottomPlane.x + inverseCameraMatrix.h*bottomPlane.y + inverseCameraMatrix.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));
}
override protected function removeFromScene():void {
if (_sortingLevel != null) {
// Удаляем пространства из уровня
delete _sortingLevel.spaces[this];
// Помечаем изменение в пространстве
_sortingLevel.changed[this] = true;
_scene.levelsToClear[_sortingLevel] = true;
// Удаляем ссылку на уровень
_sortingLevel = null;
} else {
if (node != null) {
_scene.levelsToClear[_sortingLevel] = true;
}
}
/*
// Удаляем примитив
if (primitive != null) {
// Удаляем примитив из ноды
primitive.node.removePrimitive(primitive);
// Удаляем примитив
SpaceDistancePrimitive.defer(primitive);
primitive = null;
}
*/
// Удаляем ссылку на уровень
_sortingLevel = null;
super.removeFromScene();
// Удаляем все пометки в сцене
delete _scene.spacesToGlobalTransform[this];
delete _scene.spacesToChangeSortingLevel[this];
delete _scene.spacesToChangeMaterial[this];
//delete _scene.spacesToUpdate[this];
delete _scene.spacesToUpdateMaterial[this];
}
/**
* @private
* Получить уровень по индексу. Если уровня с таким индексом нет, он создаётся.
*/
alternativa3d function getSortingLevel(index:int):SortingLevel {
var previous:SortingLevel = null;
var current:SortingLevel = firstLevel;
var level:SortingLevel;
// Перебираем уровни
while (true) {
// Если есть текущий
if (current != null) {
// Если найден уровень с требуемым индексом
if (current.index == index) {
// Возвращаем его
return current;
} else {
// Если найден уровень с большим индексом
if (current.index > index) {
// Создаём новый уровень
level = new SortingLevel();
level.space = this;
level.index = index;
if (previous != null) {
previous.next = level;
} else {
firstLevel = level;
}
level.next = current;
return level;
} else {
// Переключаемся на следующий уровень
previous = current;
current = current.next;
}
}
} else {
// Создаём новый уровень
level = new SortingLevel();
level.space = this;
level.index = index;
if (previous != null) {
previous.next = level;
} else {
firstLevel = level;
}
return level;
}
}
// null никогда не возвращается, т.к. возврат происходит из цикла
return null;
}
/**
* Уровень сортировки.
*/
public function get sortingLevel():int {
return sortingLevelIndex;
}
/**
* @private
*/
public function set sortingLevel(value:int):void {
if (sortingLevelIndex != value) {
sortingLevelIndex = value;
if (_scene != null) {
_scene.spacesToChangeSortingLevel[this] = true;
}
}
}
/**
* Режим сортировки пространства.
*/
public function get sortingMode():uint {
return _sortingMode;
}
/**
* @private
*/
public function set sortingMode(value:uint):void {
if (value > 1) {
throw new InvalidSortingModeError(value, this);
}
if (_sortingMode != value) {
_sortingMode = value;
if (_scene != null) {
_scene.spacesToChangeSortingMode[this] = true;
}
}
}
/**
* Материал пространства. При установке нового значения, устанавливаемый материал будет удалён из старого пространства.
*/
public function get material():SpaceMaterial {
return _material;
}
/**
* @private
*/
public function set material(value:SpaceMaterial):void {
if (_material != value) {
// Если был материал
if (_material != null) {
// Удалить материал
_material._space = null;
}
// Если новый материал
if (value != null) {
// Если материал был в другом пространстве
var oldSpace:Space = value._space;
if (oldSpace != null) {
// Удалить его оттуда
oldSpace._material = null;
// Если есть сцена, помечаем смену материала
if (oldSpace._scene != null) {
oldSpace._scene.spacesToChangeMaterial[oldSpace] = true;
}
}
// Добавить материал
value._space = this;
}
// Если есть сцена, помечаем смену материала
if (_scene != null) {
_scene.spacesToChangeMaterial[this] = true;
}
// Сохраняем материал
_material = value;
}
}
/**
* Имя пространства по умолчанию.
*
* @return имя пространства по умолчанию
*/
override protected function defaultName():String {
return "space" + ++counter;
}
}
}

View File

@@ -0,0 +1,246 @@
package alternativa.engine3d.core {
import alternativa.engine3d.*;
import alternativa.engine3d.sorting.SortingLevel;
import alternativa.engine3d.sorting.SpriteDistancePrimitive;
import alternativa.engine3d.materials.SpriteMaterial;
import alternativa.engine3d.sorting.DistanceNode;
use namespace alternativa3d;
public class Sprite3D extends Object3D {
// Инкремент количества объектов
private static var counter:uint = 0;
/**
* @private
* Режим сортировки
*/
alternativa3d var _sortingMode:uint = 1;
/**
* @private
* Материал
*/
alternativa3d var _material:SpriteMaterial;
/**
* @private
* Номер уровня в пространстве
*/
private var sortingLevelIndex:int = 0;
/**
* @private
* Ссылка на уровень в пространстве
*/
private var _sortingLevel:SortingLevel;
/**
* @private
* Нода в которой находится спрайт
*/
alternativa3d var node:DistanceNode;
public function Sprite3D(name:String = null) {
super(name);
}
override protected function transform():void {
super.transform();
// Если есть примитив
if (primitive != null) {
// Если изменился уровень
if (primitive.node.sortingLevel != _sortingLevel) {
// Обновляем уровень
_sortingLevel = space.getSortingLevel(sortingLevelIndex);
}
// Удаляем примитив
primitive.node.removePrimitive(primitive);
} else {
// Обновляем уровень
_sortingLevel = space.getSortingLevel(sortingLevelIndex);
// Создаём примитив
primitive = SpriteDistancePrimitive.create();
primitive.sprite = this;
}
// Добавляем примитив в уровень
_sortingLevel.distancePrimitivesToAdd.push(primitive);
// Устанавливаем координаты
primitive.coords.x = spaceMatrix.d;
primitive.coords.y = spaceMatrix.h;
primitive.coords.z = spaceMatrix.l;
// Снимаем пометки о смене уровня и материала
delete _scene.spritesToChangeSortingLevel[this];
delete _scene.spritesToChangeMaterial[this];
}
override protected function move():void {
super.move();
// Если изменился уровень
if (_sortingLevel.index != sortingLevelIndex) {
// Обновляем уровень
_sortingLevel = space.getSortingLevel(sortingLevelIndex);
}
// Удаляем примитив
primitive.node.removePrimitive(primitive);
// Добавляем примитив в уровень
_sortingLevel.distancePrimitivesToAdd.push(primitive);
// Устанавливаем координаты
primitive.coords.x = spaceMatrix.d;
primitive.coords.y = spaceMatrix.h;
primitive.coords.z = spaceMatrix.l;
// Снимаем пометки о смене уровня и материала
delete _scene.spritesToChangeSortingLevel[this];
delete _scene.spritesToChangeMaterial[this];
}
alternativa3d function changeSortingLevel():void {
trace(this, "changeSortingLevel");
// Если уровень изменился
if (_sortingLevel.index != sortingLevelIndex) {
// Обновляем уровень
_sortingLevel = space.getSortingLevel(sortingLevelIndex);
// Удаляем примитив
primitive.node.removePrimitive(primitive);
// Добавляем примитив в уровень
_sortingLevel.distancePrimitivesToAdd.push(primitive);
}
// Снимаем пометку о смене уровня
delete _scene.spacesToChangeSortingLevel[this];
}
alternativa3d function changeSortingMode():void {
trace(this, "changeSortingMode");
}
alternativa3d function changeMaterial():void {
trace(this, "changeMaterial");
}
override protected function removeFromScene(scene:Scene3D):void {
super.removeFromScene(scene);
// Удаляем примитив
if (primitive != null) {
// Удаляем примитив из ноды
primitive.node.removePrimitive(primitive);
// Удаляем примитив
SpriteDistancePrimitive.defer(primitive);
primitive = null;
}
// Удаляем ссылку на уровень
_sortingLevel = null;
// Удаляем все пометки в сцене
delete scene.spritesToChangeSortingLevel[this];
delete scene.spritesToChangeMaterial[this];
}
/**
* Уровень сортировки.
*/
public function get sortingLevel():int {
return sortingLevelIndex;
}
/**
* @private
*/
public function set sortingLevel(value:int):void {
if (sortingLevelIndex != value) {
sortingLevelIndex = value;
if (_scene != null) {
_scene.spritesToChangeSortingLevel[this] = true;
}
}
}
/**
* Режим сортировки спрайта.
*/
public function get sortingMode():uint {
return _sortingMode;
}
/**
* @private
*/
public function set sortingMode(value:uint):void {
if (_sortingMode != value) {
_sortingMode = value;
if (_scene != null) {
_scene.spritesToChangeSortingMode[this] = true;
}
}
}
/**
* Материал спрайта. При установке нового значения, устанавливаемый материал будет удалён из старого спрайта.
*/
public function get material():SpriteMaterial {
return _material;
}
/**
* @private
*/
public function set material(value:SpriteMaterial):void {
if (_material != value) {
// Если был материал
if (_material != null) {
// Удалить материал
_material._sprite = null;
}
// Если новый материал
if (value != null) {
// Если материал был в другом спрайте
var oldSprite:Sprite3D = value._sprite;
if (oldSprite != null) {
// Удалить его оттуда
oldSprite._material = null;
// Если есть сцена, помечаем смену материала
if (oldSprite._scene != null) {
oldSprite._scene.spritesToChangeMaterial[oldSprite] = true;
}
}
// Добавить материал
value._sprite = this;
}
// Если есть сцена, помечаем смену материала
if (_scene != null) {
_scene.spritesToChangeMaterial[this] = true;
}
// Сохраняем материал
_material = value;
}
}
/**
* @inheritDoc
*/
override protected function defaultName():String {
return "sprite" + ++counter;
}
}
}

View File

@@ -0,0 +1,788 @@
package alternativa.engine3d.core {
import alternativa.engine3d.*;
import alternativa.engine3d.sorting.SortingLevel;
import alternativa.engine3d.errors.FaceExistsError;
import alternativa.engine3d.errors.FaceNotFoundError;
import alternativa.engine3d.errors.InvalidIDError;
import alternativa.engine3d.materials.SurfaceMaterial;
import alternativa.types.Set;
use namespace alternativa3d;
/**
* Поверхность &mdash; набор граней, объединённых в группу. Поверхности используются для установки материалов,
* визуализирующих грани объекта.
*/
public class Surface {
/**
* @private
* Меш
*/
alternativa3d var _mesh:Mesh;
/**
* @private
* Грани
*/
alternativa3d var _faces:Set = new Set();
/**
* @private
* Ссылка на уровень в пространстве
*/
alternativa3d var _sortingLevel:SortingLevel;
/**
* @private
* Номер уровня в пространстве
*/
private var sortingLevelIndex:int = 0;
/**
* @private
* Режим сортировки
*/
alternativa3d var _sortingMode:uint = 1;
/**
* @private
* Материал
*/
alternativa3d var _material:SurfaceMaterial;
/**
* @private
* Приоритет в BSP-дереве
*/
alternativa3d var _bspLevel:int = 0;
/**
* Создание экземпляра поверхности.
*/
public function Surface() {}
alternativa3d function changeSortingLevel():void {
trace(this, "changeSortingLevel");
// Если уровень изменился
if (_sortingLevel.index != sortingLevelIndex) {
// Обновляем уровень
_sortingLevel = _mesh.space.getSortingLevel(sortingLevelIndex);
// Помечаем пространство на пересчёт
_mesh._scene.spacesToCalculate[_mesh.space] = true;
// Снимаем другие пометки поверхности
delete _mesh._scene.surfacesToChangeSortingMode[this];
delete _mesh._scene.surfacesToChangeMaterial[this];
delete _mesh._scene.surfacesToChangeBSPLevel[this];
}
// Снимаем пометку о смене уровня
delete _mesh._scene.surfacesToChangeSortingLevel[this];
}
alternativa3d function changeSortingMode():void {
trace(this, "changeSortingMode");
var surfacesToChangeMaterial:Set = _mesh._scene.surfacesToChangeMaterial;
var facesToChangeSurface:Set = _mesh._scene.facesToChangeSurface;
var facesToTransform:Set = _mesh._scene.facesToTransform;
var key:*;
var face:Face;
// Если есть материал
if (_material != null) {
// Если BSP-сортировка
if (_sortingMode == 2) {
// Обрабатываем грани поверхности
for (key in _faces) {
face = key;
// Если есть полигональный примитив
if (face.polyPrimitive != null) {
// Если грань помечена на трансформацию
if (facesToTransform[face]) {
// Расчитываем перпендикуляр грани
face.calculatePerpendicular();
// Обновление полигонального примитива
face.updatePolyPrimitive();
// Снимаем все пометки грани
delete facesToChangeSurface[face];
delete facesToTransform[face];
} else {
// Если изменилась мобильность
if (face.polyPrimitive.bspLevel != _bspLevel) {
// Обновляем мобильность примитива
face.updatePolyPrimitiveBSPLevel();
// Снимаем пометку грани на смену поверхности
delete facesToChangeSurface[face];
} else {
// Если изменился материал поверхности или грань изменила поверхность
if (surfacesToChangeMaterial[this] || facesToChangeSurface[face]) {
// Отправить полигональный примитив на перерисовку
face.redrawPolyPrimitive();
// Снимаем пометку грани на смену поверхности
delete facesToChangeSurface[face];
}
}
}
} else {
// Если есть точечный примитив
if (face.pointPrimitive != null) {
// Если грань помечена на трансформацию
if (facesToTransform[face]) {
// Расчитываем перпендикуляр грани
face.calculatePerpendicular();
// Снимаем пометку на трансформацию
delete facesToTransform[face];
}
// Смена типа примитива с точечного на полигональный
face.changePointToPolyPrimitive();
} else {
// Расчитываем перпендикуляр грани
face.calculatePerpendicular();
// Создание полигонального примитива
face.createPolyPrimitive();
}
// Снимаем пометку грани на смену поверхности
delete facesToChangeSurface[face];
}
}
} else {
// Если сортировка по расстоянию
if (_sortingMode == 1) {
// Обрабатываем грани поверхности
for (key in _faces) {
face = key;
// Если есть точечный примитив
if (face.pointPrimitive != null) {
// Если грань помечена на трансформацию
if (facesToTransform[face]) {
// Расчитываем перпендикуляр грани
face.calculatePerpendicular();
// Обновление точечного примитива
face.updatePointPrimitive();
// Снимаем все пометки грани
delete facesToChangeSurface[face];
delete facesToTransform[face];
} else {
// Если изменился материал поверхности или грань изменила поверхность
if (surfacesToChangeMaterial[this] || facesToChangeSurface[face]) {
// Отправить точечный примитив на перерисовку
face.redrawPointPrimitive();
// Снимаем пометку грани на смену поверхности
delete facesToChangeSurface[face];
}
}
} else {
// Если есть полигональный примитив
if (face.polyPrimitive != null) {
// Если грань помечена на трансформацию
if (facesToTransform[face]) {
// Расчитываем перпендикуляр грани
face.calculatePerpendicular();
// Снимаем пометку на трансформацию
delete facesToTransform[face];
}
// Смена типа примитива с полигонального на точечный
face.changePolyToPointPrimitive();
} else {
// Расчитываем перпендикуляр грани
face.calculatePerpendicular();
// Создание точечного примитива
face.createPointPrimitive();
}
// Снимаем пометку грани на смену поверхности
delete facesToChangeSurface[face];
}
}
} else {
// Если нет сортировки
}
}
} else {
// Обрабатываем грани поверхности
for (key in _faces) {
face = key;
// Если есть точечный примитив
if (face.pointPrimitive != null) {
// Удаляем точечный примитив
face.destroyPointPrimitive();
// Снимаем пометку на трансформацию
delete facesToTransform[face];
} else {
// Если есть полигональный примитив
if (face.polyPrimitive != null) {
// Удаляем полигональный примитив
face.destroyPolyPrimitive();
// Снимаем пометку на трансформацию
delete facesToTransform[face];
}
}
// Снимаем пометку грани на смену поверхности
delete facesToChangeSurface[face];
}
}
// Помечаем пространство на пересчёт
_mesh._scene.spacesToCalculate[_mesh.space] = true;
// Снимаем все пометки поверхности
delete _mesh._scene.surfacesToChangeSortingMode[this];
delete surfacesToChangeMaterial[this];
delete _mesh._scene.surfacesToChangeBSPLevel[this];
}
alternativa3d function changeMaterial():void {
trace(this, "changeMaterial");
var facesToChangeSurface:Set = _mesh._scene.facesToChangeSurface;
var facesToTransform:Set = _mesh._scene.facesToTransform;
var key:*;
var face:Face;
// Если есть материал
if (_material != null) {
// Если BSP-сортировка
if (_sortingMode == 2) {
// Обрабатываем грани поверхности
for (key in _faces) {
face = key;
// Если есть полигональный примитив
if (face.polyPrimitive != null) {
// Если грань помечена на трансформацию
if (facesToTransform[face]) {
// Расчитываем перпендикуляр грани
face.calculatePerpendicular();
// Обновление точечного примитива
face.updatePolyPrimitive();
// Снимаем пометку на трансформацию
delete facesToTransform[face];
} else {
// Если изменилась мобильность
if (face.polyPrimitive.bspLevel != _bspLevel) {
// Обновляем мобильность примитива
face.updatePolyPrimitiveBSPLevel();
} else {
// Отправить примитив на перерисовку
face.redrawPolyPrimitive();
}
}
} else {
// Если есть точечный примитив
if (face.pointPrimitive != null) {
// Если грань помечена на трансформацию
if (facesToTransform[face]) {
// Расчитываем перпендикуляр грани
face.calculatePerpendicular();
// Снимаем пометку на трансформацию
delete facesToTransform[face];
}
// Смена типа примитива с полигонального на точечный
face.changePointToPolyPrimitive();
} else {
// Расчитываем перпендикуляр грани
face.calculatePerpendicular();
// Создание точечного примитива
face.createPolyPrimitive();
}
}
// Снимаем пометку грани на смену поверхности
delete facesToChangeSurface[face];
}
} else {
// Если сортировка по расстоянию
if (_sortingMode == 1) {
// Обрабатываем грани поверхности
for (key in _faces) {
face = key;
// Если есть точечный примитив
if (face.pointPrimitive != null) {
// Если грань помечена на трансформацию
if (facesToTransform[face]) {
// Расчитываем перпендикуляр грани
face.calculatePerpendicular();
// Обновление точечного примитива
face.updatePointPrimitive();
// Снимаем пометку на трансформацию
delete facesToTransform[face];
} else {
// Отправить примитив на перерисовку
face.redrawPointPrimitive();
}
} else {
// Если есть полигональный примитив
if (face.polyPrimitive != null) {
// Если грань помечена на трансформацию
if (facesToTransform[face]) {
// Расчитываем перпендикуляр грани
face.calculatePerpendicular();
// Снимаем пометку на трансформацию
delete facesToTransform[face];
}
// Смена типа примитива с полигонального на точечный
face.changePolyToPointPrimitive();
} else {
// Расчитываем перпендикуляр грани
face.calculatePerpendicular();
// Создание точечного примитива
face.createPointPrimitive();
}
}
// Снимаем пометку грани на смену поверхности
delete facesToChangeSurface[face];
}
} else {
// Если нет сортировки
}
}
} else {
// Обрабатываем грани поверхности
for (key in _faces) {
face = key;
// Если есть точечный примитив
if (face.pointPrimitive != null) {
// Удаляем точечный примитив
face.destroyPointPrimitive();
// Снимаем пометку на трансформацию
delete facesToTransform[face];
} else {
// Если есть полигональный примитив
if (face.polyPrimitive != null) {
// Удаляем полигональный примитив
face.destroyPolyPrimitive();
// Снимаем пометку на трансформацию
delete facesToTransform[face];
}
}
// Снимаем пометку грани на смену поверхности
delete facesToChangeSurface[face];
}
}
// Помечаем пространство на пересчёт
_mesh._scene.spacesToCalculate[_mesh.space] = true;
// Снимаем все пометки поверхности
delete _mesh._scene.surfacesToChangeMaterial[this];
delete _mesh._scene.surfacesToChangeBSPLevel[this];
}
alternativa3d function changeBSPLevel():void {
trace(this, "changeBSPLevel");
var facesToChangeSurface:Set = _mesh._scene.facesToChangeSurface;
var facesToTransform:Set = _mesh._scene.facesToTransform;
var key:*;
var face:Face;
// Если есть материал
if (_material != null) {
// Если BSP-сортировка
if (_sortingMode == 2) {
// Обрабатываем грани поверхности
for (key in _faces) {
face = key;
// Если есть полигональный примитив
if (face.polyPrimitive != null) {
// Если грань помечена на трансформацию
if (facesToTransform[face]) {
// Расчитываем перпендикуляр грани
face.calculatePerpendicular();
// Обновление полигонального примитива
face.updatePolyPrimitive();
// Снимаем все пометки грани
delete facesToChangeSurface[face];
delete facesToTransform[face];
} else {
// Если изменилась мобильность
if (face.polyPrimitive.bspLevel != _bspLevel) {
// Обновляем мобильность примитива
face.updatePolyPrimitiveBSPLevel();
// Снимаем пометку грани на смену поверхности
delete facesToChangeSurface[face];
} else {
// Если грань изменила поверхность
if (facesToChangeSurface[face]) {
// Отправить полигональный примитив на перерисовку
face.redrawPolyPrimitive();
// Снимаем пометку грани на смену поверхности
delete facesToChangeSurface[face];
}
}
}
} else {
// Если есть точечный примитив
if (face.pointPrimitive != null) {
// Если грань помечена на трансформацию
if (facesToTransform[face]) {
// Расчитываем перпендикуляр грани
face.calculatePerpendicular();
// Снимаем пометку на трансформацию
delete facesToTransform[face];
}
// Смена типа примитива с точечного на полигональный
face.changePointToPolyPrimitive();
} else {
// Расчитываем перпендикуляр грани
face.calculatePerpendicular();
// Создание полигонального примитива
face.createPolyPrimitive();
}
// Снимаем пометку грани на смену поверхности
delete facesToChangeSurface[face];
}
}
} else {
// Если сортировка по расстоянию
if (_sortingMode == 1) {
// Обрабатываем грани поверхности
for (key in _faces) {
face = key;
// Если есть точечный примитив
if (face.pointPrimitive != null) {
// Если грань помечена на трансформацию
if (facesToTransform[face]) {
// Расчитываем перпендикуляр грани
face.calculatePerpendicular();
// Обновление точечного примитива
face.updatePointPrimitive();
// Снимаем все пометки грани
delete facesToChangeSurface[face];
delete facesToTransform[face];
} else {
// Если грань изменила поверхность
if (facesToChangeSurface[face]) {
// Отправить точечный примитив на перерисовку
face.redrawPointPrimitive();
// Снимаем пометку грани на смену поверхности
delete facesToChangeSurface[face];
}
}
} else {
// Если есть полигональный примитив
if (face.polyPrimitive != null) {
// Если грань помечена на трансформацию
if (facesToTransform[face]) {
// Расчитываем перпендикуляр грани
face.calculatePerpendicular();
// Снимаем пометку на трансформацию
delete facesToTransform[face];
}
// Смена типа примитива с полигонального на точечный
face.changePolyToPointPrimitive();
} else {
// Расчитываем перпендикуляр грани
face.calculatePerpendicular();
// Создание точечного примитива
face.createPointPrimitive();
}
// Снимаем пометку грани на смену поверхности
delete facesToChangeSurface[face];
}
}
} else {
// Если нет сортировки
}
}
} else {
// Обрабатываем грани поверхности
for (key in _faces) {
face = key;
// Если есть точечный примитив
if (face.pointPrimitive != null) {
// Удаляем точечный примитив
face.destroyPointPrimitive();
// Снимаем пометку на трансформацию
delete facesToTransform[face];
} else {
// Если есть полигональный примитив
if (face.polyPrimitive != null) {
// Удаляем полигональный примитив
face.destroyPolyPrimitive();
// Снимаем пометку на трансформацию
delete facesToTransform[face];
}
}
// Снимаем пометку грани на смену поверхности
delete facesToChangeSurface[face];
}
}
// Помечаем пространство на пересчёт
_mesh._scene.spacesToCalculate[_mesh.space] = true;
// Снимаем все пометки поверхности
delete _mesh._scene.surfacesToChangeBSPLevel[this];
}
/**
* Добавление грани в поверхность.
*
* @param face экземпляр класса <code>alternativa.engine3d.core.Face</code> или идентификатор грани полигонального объекта
*
* @throws alternativa.engine3d.errors.FaceNotFoundError грань не найдена в полигональном объекте содержащем поверхность
* @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
* @throws alternativa.engine3d.errors.FaceExistsError поверхность уже содержит указанную грань
*
* @see Face
*/
public function addFace(face:Object):void {
var byLink:Boolean = face is Face;
// Проверяем на нахождение поверхности в меше
if (_mesh == null) {
throw new FaceNotFoundError(face, this);
}
// Проверяем на null
if (face == null) {
throw new FaceNotFoundError(null, this);
}
// Проверяем наличие грани в меше
if (byLink) {
// Если удаляем по ссылке
if (Face(face)._mesh != _mesh) {
// Если грань не в меше
throw new FaceNotFoundError(face, this);
}
} else {
// Если удаляем по ID
if (_mesh._faces[face] == undefined) {
// Если нет грани с таким ID
throw new FaceNotFoundError(face, this);
} else {
if (!(_mesh._faces[face] is Face)) {
throw new InvalidIDError(face, this);
}
}
}
// Находим грань
var f:Face = byLink ? Face(face) : _mesh._faces[face];
// Проверяем наличие грани в поверхности
if (_faces[f]) {
// Если грань уже в поверхности
throw new FaceExistsError(f, this);
}
// Проверяем грань на нахождение в другой поверхности
if (f._surface != null) {
// Удаляем её из той поверхности
delete f._surface._faces[f];
}
// Добавляем грань в поверхность
_faces[f] = true;
// Указываем поверхность грани
f._surface = this;
// Помечаем грань на смену поверхности
if (_mesh != null && _mesh._scene != null) {
_mesh._scene.facesToChangeSurface[f] = true;
}
}
/**
* Удаление грани из поверхности.
*
* @param face экземпляр класса <code>alternativa.engine3d.core.Face</code> или идентификатор грани полигонального объекта
*
* @throws alternativa.engine3d.errors.FaceNotFoundError поверхность не содержит указанную грань
* @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
*
* @see Face
*/
public function removeFace(face:Object):void {
var byLink:Boolean = face is Face;
// Проверяем на нахождение поверхности в меше
if (_mesh == null) {
throw new FaceNotFoundError(face, this);
}
// Проверяем на null
if (face == null) {
throw new FaceNotFoundError(null, this);
}
// Проверяем наличие грани в меше
if (byLink) {
// Если удаляем по ссылке
if (Face(face)._mesh != _mesh) {
// Если грань не в меше
throw new FaceNotFoundError(face, this);
}
} else {
// Если удаляем по ID
if (_mesh._faces[face] == undefined) {
// Если нет грани с таким ID
throw new FaceNotFoundError(face, this);
} else {
if (!(_mesh._faces[face] is Face)) {
throw new InvalidIDError(face, this);
}
}
}
// Находим грань
var f:Face = byLink ? Face(face) : _mesh._faces[face];
// Проверяем наличие грани в поверхности
if (!_faces[f]) {
// Если грань не в поверхности
throw new FaceNotFoundError(f, this);
}
// Удаляем грань из поверхности
delete _faces[f];
// Удаляем ссылку на поверхность грани
f._surface = null;
// Помечаем грань на смену поверхности
if (_mesh != null && _mesh._scene != null) {
_mesh._scene.facesToChangeSurface[f] = true;
}
}
/**
* Полигональный объект, которому принадлежит поверхность.
*/
public function get mesh():Mesh {
return _mesh;
}
/**
* Набор граней поверхности.
*/
public function get faces():Set {
return _faces.clone();
}
/**
* Уровень сортировки.
*/
public function get sortingLevel():int {
return sortingLevelIndex;
}
/**
* @private
*/
public function set sortingLevel(value:int):void {
if (sortingLevelIndex != value) {
sortingLevelIndex = value;
if (_mesh != null && _mesh._scene != null) {
_mesh._scene.surfacesToChangeSortingLevel[this] = true;
}
}
}
/**
* Режим сортировки граней поверхности.
*/
public function get sortingMode():uint {
return _sortingMode;
}
/**
* @private
*/
public function set sortingMode(value:uint):void {
if (_sortingMode != value) {
_sortingMode = value;
if (_mesh != null && _mesh._scene != null) {
_mesh._scene.surfacesToChangeSortingMode[this] = true;
}
}
}
/**
* Материал поверхности. При установке нового значения, устанавливаемый материал будет удалён из старой поверхности.
*/
public function get material():SurfaceMaterial {
return _material;
}
/**
* @private
*/
public function set material(value:SurfaceMaterial):void {
if (_material != value) {
// Если был материал
if (_material != null) {
// Удалить материал из поверхности
_material._surface = null;
}
// Если новый материал
if (value != null) {
// Если материал был в другой поверхности
var oldSurface:Surface = value._surface;
if (oldSurface != null) {
// Удалить его оттуда
oldSurface._material = null;
// Если есть сцена, помечаем смену материала
if (oldSurface._mesh != null && oldSurface._mesh._scene != null) {
oldSurface._mesh._scene.surfacesToChangeMaterial[oldSurface] = true;
}
}
// Добавить материал в поверхность
value._surface = this;
}
// Если есть сцена, помечаем смену материала
if (_mesh != null && _mesh._scene != null) {
_mesh._scene.surfacesToChangeMaterial[this] = true;
}
// Сохраняем материал
_material = value;
}
}
/**
* Приоритет BSP. Приоритет влияет на положение граней в BSP-дереве.
*/
public function get bspLevel():int {
return _bspLevel;
}
/**
* @private
*/
public function set bspLevel(value:int):void {
if (_bspLevel != value) {
_bspLevel = value;
if (_mesh != null && _mesh._scene != null) {
_mesh._scene.surfacesToChangeBSPLevel[this] = true;
}
}
}
/**
* Идентификатор поверхности в полигональном объекте. Если поверхность не принадлежит ни одному объекту,
* значение идентификатора равно <code>null</code>.
*/
public function get id():Object {
return (_mesh != null) ? _mesh.getSurfaceId(this) : null;
}
/**
* Строковое представление объекта.
*
* @return строковое представление объекта
*/
public function toString():String {
var length:uint = _faces.length;
var res:String = "[Surface ID:" + id + ((length > 0) ? " faces:" : "");
var i:uint = 0;
for (var key:* in _faces) {
var face:Face = key;
res += face.id + ((i < length - 1) ? ", " : "");
i++;
}
res += "]";
return res;
}
}
}

View File

@@ -0,0 +1,174 @@
package alternativa.engine3d.core {
import alternativa.engine3d.*;
import alternativa.types.Matrix3D;
import alternativa.types.Point3D;
import alternativa.types.Set;
use namespace alternativa3d;
/**
* Вершина полигона в трёхмерном пространстве. Вершина хранит свои координаты, а также ссылки на
* полигональный объект и грани этого объекта, которым она принадлежит.
*/
final public class Vertex {
/**
* @private
* Меш
*/
alternativa3d var _mesh:Mesh;
/**
* @private
* Координаты точки
*/
alternativa3d var _coords:Point3D = new Point3D();
/**
* @private
* Грани
*/
alternativa3d var _faces:Set = new Set();
/**
* @private
* Координаты в сцене
*/
alternativa3d var spaceCoords:Point3D = new Point3D();
/**
* Создание экземпляра вершины.
*
* @param x координата вершины по оси X
* @param y координата вершины по оси Y
* @param z координата вершины по оси Z
*/
public function Vertex() {}
// Вызывается при изменении координат вершины, как вручную, так и при трансформации меша
alternativa3d function move():void {
trace(this, "move");
spaceCoords.copy(_coords);
spaceCoords.transform(_mesh.spaceMatrix);
delete _mesh._scene.verticesToMove[this];
}
/**
* @private
*/
public function set x(value:Number):void {
if (_coords.x != value) {
_coords.x = value;
markToMove();
}
}
/**
* @private
*/
public function set y(value:Number):void {
if (_coords.y != value) {
_coords.y = value;
markToMove();
}
}
/**
* @private
*/
public function set z(value:Number):void {
if (_coords.z != value) {
_coords.z = value;
markToMove();
}
}
/**
* @private
*/
public function set coords(value:Point3D):void {
if (!_coords.equals(value)) {
_coords.copy(value);
markToMove();
}
}
/**
* Добавление сигнала об изменении координат вершины в сцену.
* Также добавляются сигналы на трансформацию для зависимых видимых граней.
*/
private function markToMove():void {
var scene:Scene3D;
if (_mesh != null && (scene = _mesh._scene) != null) {
// Пометка вершины на перемещение
scene.verticesToMove[this] = true;
for (var key:* in _faces) {
var face:Face = key;
if (face.pointPrimitive != null || face.polyPrimitive != null) {
// Пометка зависимой грани, если у неё есть примитив
scene.facesToTransform[face] = true;
}
}
}
}
/**
* Координата вершины по оси X.
*/
public function get x():Number {
return _coords.x;
}
/**
* Координата вершины по оси Y.
*/
public function get y():Number {
return _coords.y;
}
/**
* Координата вершины по оси Z.
*/
public function get z():Number {
return _coords.z;
}
/**
* Координаты вершины.
*/
public function get coords():Point3D {
return _coords.clone();
}
/**
* Полигональный объект, которому принадлежит вершина.
*/
public function get mesh():Mesh {
return _mesh;
}
/**
* Множество граней, которым принадлежит вершина. Каждый элемент множества является объектом класса
* <code>altertnativa.engine3d.core.Face</code>.
*
* @see Face
*/
public function get faces():Set {
return _faces.clone();
}
/**
* Идентификатор вершины в полигональном объекте. Если вершина не принадлежит полигональному объекту, возвращается <code>null</code>.
*/
public function get id():Object {
return (_mesh != null) ? _mesh.getVertexId(this) : null;
}
/**
* Строковое представление объекта.
*
* @return строковое представление объекта
*/
public function toString():String {
return "[Vertex ID:" + id + " " + _coords.x.toFixed(2) + ", " + _coords.y.toFixed(2) + ", " + _coords.z.toFixed(2) + "]";
}
}
}

View File

@@ -0,0 +1,73 @@
package alternativa.engine3d.display {
import alternativa.engine3d.*;
import alternativa.engine3d.core.Space;
use namespace alternativa3d;
public class Canvas extends DisplayItem {
/**
* @private
* Пространство
*/
alternativa3d var space:Space;
/**
* @private
* Ссылка на предыдущий объект
*/
alternativa3d var previous:DisplayItem;
// Список отрисовочных объектов
alternativa3d var firstItem:DisplayItem;
alternativa3d var previousItem:DisplayItem;
alternativa3d var currentItem:DisplayItem;
// Хранилище неиспользуемых канвасов
static private var collector:Array = new Array();
/**
* @private
* Создание полотна.
*/
static alternativa3d function create():Canvas {
var canvas:Canvas;
if ((canvas = collector.pop()) == null) {
// Если коллектор пуст, создаём новый канвас
return new Canvas();
}
return canvas;
}
/**
* @private
* Удаление и зачистка канваса.
*/
static alternativa3d function destroy(canvas:Canvas):void {
// Зачистка списка
var item:DisplayItem = canvas.firstItem;
while (item != null) {
// Сохраняем следующий
var next:DisplayItem = item.next;
// Удаляем из канваса
canvas.removeChild(item);
// Удаляем
(item is Skin) ? Skin.destroy(item as Skin) : Canvas.destroy(item as Canvas);
// Следующий устанавливаем текущим
item = next;
}
// Зачищаем ссылки
canvas.next = null;
canvas.previous = null;
canvas.space = null;
// Удаляем список
canvas.firstItem = null;
canvas.previousItem = null;
canvas.currentItem = null;
// Отправляем в хранилище
collector.push(canvas);
}
}
}

View File

@@ -0,0 +1,18 @@
package alternativa.engine3d.display {
import alternativa.engine3d.*;
import flash.display.Sprite;
use namespace alternativa3d;
/**
* @private
*/
public class DisplayItem extends Sprite {
/**
* @private
* Ссылка на следующий объект
*/
alternativa3d var next:DisplayItem;
}
}

View File

@@ -0,0 +1,68 @@
package alternativa.engine3d.display {
import alternativa.engine3d.*;
import alternativa.engine3d.materials.Material;
import alternativa.engine3d.sorting.Primitive;
import flash.display.Graphics;
use namespace alternativa3d;
/**
* @private
* Контейнер, используемый материалами для отрисовки примитивов.
* Каждый примитив BSP-дерева рисуется в своём контейнере.
*/
public class Skin extends DisplayItem {
/**
* @private
* Графика скина (для быстрого доступа)
*/
alternativa3d var gfx:Graphics = graphics;
/**
* @private
* Примитив
*/
//alternativa3d var primitive:Primitive;
/**
* @private
* Материал, связанный со скином.
*/
alternativa3d var material:Material;
// Хранилище неиспользуемых скинов
static private var collector:Array = new Array();
/**
* @private
* Создание скина.
*/
static alternativa3d function create():Skin {
var skin:Skin;
if ((skin = collector.pop()) == null) {
// Если коллектор пуст, создаём новый скин
return new Skin();
}
return skin;
}
/**
* @private
* Удаление скина.
*/
static alternativa3d function destroy(skin:Skin):void {
// Очистка скина
if (skin.material != null) {
skin.material.clear(skin);
}
// Зачищаем ссылки
skin.next = null;
//skin.primitive = null;
skin.material = null;
// Отправляем в хранилище
collector.push(skin);
}
}
}

View File

@@ -0,0 +1,117 @@
package alternativa.engine3d.display {
import alternativa.engine3d.*;
import alternativa.engine3d.core.Camera3D;
import flash.display.Sprite;
use namespace alternativa3d;
/**
* Область для вывода изображения с камеры.
*/
public class View extends Sprite {
/**
* @private
* Область отрисовки пространства сцены
*/
alternativa3d var canvas:Canvas;
alternativa3d var _camera:Camera3D;
/**
* @private
* Ширина области вывода
*/
alternativa3d var _width:Number;
/**
* @private
* Высота области вывода
*/
alternativa3d var _height:Number;
/**
* Создание экземпляра области вывода.
*
* @param camera камера, изображение с которой должно выводиться
* @param width ширина области вывода
* @param height высота области вывода
*/
public function View(camera:Camera3D = null, width:Number = 0, height:Number = 0) {
mouseChildren = false;
tabChildren = false;
this.camera = camera;
this.width = width;
this.height = height;
}
/**
* Камера с которой ведётся отображение.
*/
public function get camera():Camera3D {
return _camera;
}
/**
* @private
*/
public function set camera(value:Camera3D):void {
if (value != _camera) {
if (_camera != null) {
_camera.view = null;
}
if (value != null) {
value.view = this;
}
}
}
/**
* Ширина области вывода в пикселях.
*/
override public function get width():Number {
return _width;
}
/**
* @private
*/
override public function set width(value:Number):void {
if (_width != value) {
_width = value;
if (canvas != null) {
canvas.x = _width*0.5;
}
if (_camera != null) {
// Отправляем сигнал об изменении плоскостей отсечения
// ...
}
}
}
/**
* Высота области вывода в пикселях.
*/
override public function get height():Number {
return _height;
}
/**
* @private
*/
override public function set height(value:Number):void {
if (_height != value) {
_height = value;
if (canvas != null) {
canvas.y = _height*0.5;
}
if (_camera != null) {
// Отправляем сигнал об изменении плоскостей отсечения
// ...
}
}
}
}
}

View File

@@ -0,0 +1,27 @@
package alternativa.engine3d.errors {
import alternativa.utils.TextUtils;
/**
* Базовый класс для ошибок 3d-engine.
*/
public class Alternativa3DError extends Error {
/**
* Источник ошибки - объект в котором произошла ошибка.
*/
public var source:Object;
/**
* Создание экземпляра класса.
*
* @param message описание ошибки
* @param source источник ошибки
*/
public function Alternativa3DError(message:String = "", source:Object = null) {
super(message);
this.source = source;
this.name = "Alternativa3DError";
}
}
}

View File

@@ -0,0 +1,35 @@
package alternativa.engine3d.errors {
import alternativa.engine3d.core.Face;
import alternativa.engine3d.core.Mesh;
import alternativa.utils.TextUtils;
import alternativa.engine3d.core.Surface;
/**
* Ошибка, возникающая при попытке добавить в какой-либо объект грань, уже содержащуюся в данном объекте.
*/
public class FaceExistsError extends ObjectExistsError {
/**
* Создание экземпляра класса.
*
* @param face экземпляр или идентификатор грани, которая уже содержится в объекте
* @param source источник ошибки
*/
public function FaceExistsError(face:Object = null, source:Object = null) {
var message:String;
if (source is Mesh) {
message = "Mesh ";
} else if (source is Surface) {
message = "Surface ";
}
if (face is Face) {
message += "%1. Face %2 already exists.";
} else {
message += "%1. Face with ID '%2' already exists.";
}
super(TextUtils.insertVars(message, source, face), face, source);
this.name = "FaceExistsError";
}
}
}

View File

@@ -0,0 +1,29 @@
package alternativa.engine3d.errors {
import alternativa.engine3d.core.Mesh;
import alternativa.utils.TextUtils;
/**
* Ошибка, обозначающая недостаточное количество вершин для создания грани.
* Для создания грани должно быть указано не менее трех вершин.
*/
public class FaceNeedMoreVerticesError extends Alternativa3DError {
/**
* Количество переданных для создания грани вершин
*/
public var count:uint;
/**
* Создание экземпляра класса.
*
* @param mesh объект, в котором произошла ошибка
* @param count количество вершин, переданное для создания грани
*/
public function FaceNeedMoreVerticesError(mesh:Mesh = null, count:uint = 0) {
super(TextUtils.insertVars("Mesh %1. %2 vertices not enough for face creation.", mesh, count), mesh);
this.count = count;
this.name = "FaceNeedMoreVerticesError";
}
}
}

View File

@@ -0,0 +1,34 @@
package alternativa.engine3d.errors {
import alternativa.engine3d.core.Face;
import alternativa.engine3d.core.Mesh;
import alternativa.utils.TextUtils;
/**
* Ошибка, возникающая, если грань не найдена в объекте.
*/
public class FaceNotFoundError extends ObjectNotFoundError {
/**
* Создание экземпляра класса.
*
* @param face экземпляр или идентификатор грани
* @param source объект, в котором произошла ошибка
*/
public function FaceNotFoundError(face:Object = null, source:Object = null) {
var message:String;
if (source is Mesh) {
message = "Mesh ";
} else {
message = "Surface ";
}
if (face is Face) {
message += "%1. Face %2 not found.";
} else {
message += "%1. Face with ID '%2' not found.";
}
super(TextUtils.insertVars(message, source, face), face, source);
this.name = "FaceNotFoundError";
}
}
}

View File

@@ -0,0 +1,34 @@
package alternativa.engine3d.errors {
import alternativa.utils.TextUtils;
import alternativa.engine3d.core.Mesh;
import alternativa.engine3d.core.Surface;
/**
* Ошибка, обозначающая, что идентификатор зарезервирован и не может быть использован.
*/
public class InvalidIDError extends Engine3DError {
/**
* Зарезервированный идентификатор
*/
public var id:Object;
/**
* Создание экземпляра класса.
*
* @param id идентификатор
* @param source объект, в котором произошла ошибка
*/
public function InvalidIDError(id:Object = null, source:Object = null) {
var message:String;
if (source is Mesh) {
message = "Mesh %2. ";
} else if (source is Surface) {
message = "Surface %2. ";
}
super(TextUtils.insertVars(message + "ID %1 is reserved and cannot be used", [id, source]), source);
this.id = id;
this.name = "InvalidIDError";
}
}
}

View File

@@ -0,0 +1,43 @@
package alternativa.engine3d.errors {
import alternativa.engine3d.core.Space;
import alternativa.utils.TextUtils;
import alternativa.engine3d.sorting.SortingMode;
/**
* Ошибка, обозначающая, что режим сортировки не может быть использован.
*/
public class InvalidSortingModeError extends Alternativa3DError {
/**
* Режим сортировки
*/
public var sortingMode:uint;
/**
* Создание экземпляра класса.
*
* @param sortingMode режим сортировки
* @param source объект, в котором произошла ошибка
*/
public function InvalidSortingModeError(sortingMode:uint = 0, source:Object = null) {
var message:String;
if (source is Space) {
message = "Space %2. ";
} else {
/*
if (source is Sprite3D) {
message = "Sprite3D %2. ";
} else {
if (source is Sprite3D) {
message = "Surface %2. ";
}
}
*/
}
super(TextUtils.insertVars(message + "Sorting mode %1 cannot be used", sortingMode, source));
this.sortingMode = sortingMode;
this.name = "InvalidSortingModeError";
}
}
}

View File

@@ -0,0 +1,28 @@
package alternativa.engine3d.errors {
import alternativa.engine3d.core.Object3D;
import alternativa.utils.TextUtils;
/**
* Ошибка, связанная с нарушением иерархии объектов сцены.
*/
public class Object3DHierarchyError extends Alternativa3DError {
/**
* Объект сцены, нарушающий иерархию
*/
public var object:Object3D;
/**
* Создание экземпляра класса.
*
* @param object объект, нарушающий иерархию
* @param source источник ошибки
*/
public function Object3DHierarchyError(object:Object3D = null, source:Object3D = null) {
super(TextUtils.insertVars("Object3D %1. Object %2 cannot be added", source, object), source);
this.object = object;
this.name = "Object3DHierarchyError";
}
}
}

View File

@@ -0,0 +1,22 @@
package alternativa.engine3d.errors {
import alternativa.engine3d.core.Object3D;
import alternativa.utils.TextUtils;
/**
* Ошибка, возникающая, когда объект сцены не был найден в списке связанных с необходимым объектом сцены.
*/
public class Object3DNotFoundError extends ObjectNotFoundError {
/**
* Создание экземпляра класса.
*
* @param object ненайденный объект сцены
* @param source объект сцены, в котором произошла ошибка
*/
public function Object3DNotFoundError(object:Object3D = null, source:Object3D = null) {
super(TextUtils.insertVars("Object3D %1. Object %2 not in child list", source, object), object, source);
this.name = "Object3DNotFoundError";
}
}
}

View File

@@ -0,0 +1,26 @@
package alternativa.engine3d.errors {
/**
* Ошибка, обозначающая, что объект уже присутствует в контейнере.
*/
public class ObjectExistsError extends Alternativa3DError {
/**
* Экземпляр или идентификатор объекта, который уже присутствует в контейнере
*/
public var object:Object;
/**
* Создание экземпляра класса.
*
* @param message описание ошибки
* @param object объект, который уже присутствует в контейнере
* @param source объект, вызвавший ошибку
*/
public function ObjectExistsError(message:String = "", object:Object = null, source:Object = null) {
super(message, source);
this.object = object;
this.name = "ObjectExistsError";
}
}
}

View File

@@ -0,0 +1,26 @@
package alternativa.engine3d.errors {
/**
* Необходимый объект не был найден в контейнере.
*/
public class ObjectNotFoundError extends Alternativa3DError {
/**
* Объект, который отсутствует в контейнере.
*/
public var object:Object;
/**
* Создание экземпляра класса.
*
* @param message описание ошибки
* @param object отсутствующий объект
* @param source объект, вызвавший ошибку
*/
public function ObjectNotFoundError(message:String = "", object:Object = null, source:Object = null) {
super(message, source);
this.object = object;
this.name = "ObjectNotFoundError";
}
}
}

View File

@@ -0,0 +1,22 @@
package alternativa.engine3d.errors {
import alternativa.engine3d.core.Mesh;
import alternativa.utils.TextUtils;
/**
* Ошибка, обозначающая, что поверхность уже присутствует в контейнере.
*/
public class SurfaceExistsError extends ObjectExistsError {
/**
* Создание экземпляра класса.
*
* @param surface поверхность, которая уже присутствует в контейнере
* @param mesh объект, вызвавший ошибку
*/
public function SurfaceExistsError(surface:Object = null, mesh:Mesh = null) {
super(TextUtils.insertVars("Mesh %1. Surface with ID '%2' already exists.", mesh, surface), surface, mesh);
this.name = "SurfaceExistsError";
}
}
}

View File

@@ -0,0 +1,31 @@
package alternativa.engine3d.errors {
import alternativa.engine3d.core.Mesh;
import alternativa.engine3d.core.Surface;
import alternativa.utils.TextUtils;
/**
* Ошибка, обозначающая, что поверхность не найдена в контейнере.
*/
public class SurfaceNotFoundError extends ObjectNotFoundError {
/**
* Создание экземпляра класса.
*
* @param surface поверхность, которая отсутствует в объекте
* @param mesh объект, который вызвал ошибку
*/
public function SurfaceNotFoundError(surface:Object = null, mesh:Mesh = null) {
if (mesh == null) {
}
if (surface is Surface) {
message = "Mesh %1. Surface %2 not found.";
} else {
message = "Mesh %1. Surface with ID '%2' not found.";
}
super(TextUtils.insertVars(message, mesh, surface), surface, mesh);
this.name = "SurfaceNotFoundError";
}
}
}

View File

@@ -0,0 +1,22 @@
package alternativa.engine3d.errors {
import alternativa.engine3d.core.Mesh;
import alternativa.utils.TextUtils;
/**
* Ошибка, обозначающая, что вершина уже содержится в объекте.
*/
public class VertexExistsError extends ObjectExistsError {
/**
* Создание экземпляра класса.
*
* @param vertex вершина, которая уже есть в объекте
* @param mesh объект, вызвавший ошибку
*/
public function VertexExistsError(vertex:Object = null, mesh:Mesh = null) {
super(TextUtils.insertVars("Mesh %1. Vertex with ID '%2' already exists.", mesh, vertex), vertex, mesh);
this.name = "VertexExistsError";
}
}
}

View File

@@ -0,0 +1,28 @@
package alternativa.engine3d.errors {
import alternativa.engine3d.core.Mesh;
import alternativa.engine3d.core.Vertex;
import alternativa.utils.TextUtils;
/**
* Ошибка, обозначающая, что вершина не найдена в объекте.
*/
public class VertexNotFoundError extends ObjectNotFoundError {
/**
* Создание экземпляра класса.
*
* @param vertex вершина, которая не найдена в объекте
* @param mesh объект, вызвавший ошибку
*/
public function VertexNotFoundError(vertex:Object = null, mesh:Mesh = null) {
if (vertex is Vertex) {
message = "Mesh %1. Vertex %2 not found.";
} else {
message = "Mesh %1. Vertex with ID '%2' not found.";
}
super(TextUtils.insertVars(message, mesh, vertex), vertex, mesh);
this.name = "VertexNotFoundError";
}
}
}

View File

@@ -0,0 +1,197 @@
package alternativa.engine3d.materials {
import alternativa.engine3d.*;
import flash.display.BlendMode;
import alternativa.engine3d.core.Space;
import alternativa.engine3d.display.Skin;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.sorting.Primitive;
import alternativa.types.Point3D;
use namespace alternativa3d;
/**
* Материал, заполняющий полигон сплошной одноцветной заливкой.
*/
public class FillMaterial extends SurfaceMaterial {
/**
* @private
* Цвет
*/
alternativa3d var _color:uint;
// Вспомогательные массивы точек для отрисовки
private var points1:Array = new Array();
private var points2:Array = new Array();
/**
* Создание экземпляра класса.
*
* @param color цвет заливки
* @param alpha коэффициент непрозрачности материала. Значение 1 соответствует полной непрозрачности, значение 0 соответствует полной прозрачности.
* @param blendMode режим наложения цвета
*/
public function FillMaterial(color:uint, alpha:Number = 1, blendMode:String = BlendMode.NORMAL) {
_color = color;
_colorTransform.alphaMultiplier = alpha;
_blendMode = blendMode;
}
alternativa3d function draw(space:Space, camera:Camera3D, skin:Skin, primitive:Primitive):Boolean {
/*var i:uint;
var length:uint = primitive.num;
var point:Point3D;
// Формируем список точек полигона
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;
}
}*/
return false;
}
/**
* @private
* @inheritDoc
*/
override protected function clip(length:uint, source:Array, target:Array, plane:Point3D, offset:Number):uint {
var k:Number;
var point:Point3D;
var index:uint = 0;
var point1:Point3D = source[length - 1];
var offset1:Number = plane.x*point1.x + plane.y*point1.y + plane.z*point1.z - offset;
for (var i:uint = 0; i < length; i++) {
var point2:Point3D = source[i];
var offset2:Number = plane.x*point2.x + plane.y*point2.y + plane.z*point2.z - offset;
if (offset2 > 0) {
if (offset1 <= 0) {
k = offset2/(offset2 - offset1);
point = target[index];
if (point == null) {
point = new Point3D(point2.x - (point2.x - point1.x)*k, point2.y - (point2.y - point1.y)*k, point2.z - (point2.z - point1.z)*k);
target[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 = target[index];
if (point == null) {
point = new Point3D(point2.x, point2.y, point2.z);
target[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 = target[index];
if (point == null) {
point = new Point3D(point2.x - (point2.x - point1.x)*k, point2.y - (point2.y - point1.y)*k, point2.z - (point2.z - point1.z)*k);
target[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
* @inheritDoc
*/
/*override alternativa3d function draw(camera:Camera3D, skin:Skin, length:uint, points:Array):void {
skin.alpha = _alpha;
skin.blendMode = _blendMode;
var i:uint;
var point:DrawPoint;
var gfx:Graphics = skin.gfx;
if (camera._orthographic) {
gfx.beginFill(_color);
if (_wireThickness >= 0) {
gfx.lineStyle(_wireThickness, _wireColor);
}
point = points[0];
gfx.moveTo(point.x, point.y);
for (i = 1; i < length; i++) {
point = points[i];
gfx.lineTo(point.x, point.y);
}
if (_wireThickness >= 0) {
point = points[0];
gfx.lineTo(point.x, point.y);
}
} else {
gfx.beginFill(_color);
if (_wireThickness >= 0) {
gfx.lineStyle(_wireThickness, _wireColor);
}
point = points[0];
var perspective:Number = camera.focalLength/point.z;
gfx.moveTo(point.x*perspective, point.y*perspective);
for (i = 1; i < length; i++) {
point = points[i];
perspective = camera.focalLength/point.z;
gfx.lineTo(point.x*perspective, point.y*perspective);
}
if (_wireThickness >= 0) {
point = points[0];
perspective = camera.focalLength/point.z;
gfx.lineTo(point.x*perspective, point.y*perspective);
}
}
}*/
/**
* Цвет заливки.
*/
public function get color():uint {
return _color;
}
/**
* @private
*/
public function set color(value:uint):void {
if (_color != value) {
_color = value;
markToChange();
}
}
/**
* @inheritDoc
*/
override protected function create():Material {
return new FillMaterial(_color);
}
}
}

View File

@@ -0,0 +1,169 @@
package alternativa.engine3d.materials {
import alternativa.engine3d.*;
import alternativa.engine3d.display.Skin;
import flash.display.BlendMode;
import flash.geom.ColorTransform;
import alternativa.engine3d.display.DisplayItem;
use namespace alternativa3d;
/**
* Базовый класс для материалов.
*/
public class Material {
/**
* @private
* Видимость
*/
alternativa3d var _visible:Boolean = true;
/**
* @private
* Режим наложения
*/
alternativa3d var _blendMode:String = BlendMode.NORMAL;
/**
* @private
* Трансформация цвета
*/
alternativa3d var _colorTransform:ColorTransform = new ColorTransform();
// Флаг трансформации цвета по умолчанию
private var defaultColorTransform:Boolean = true;
/**
* Видимость.
*/
public function get visible():Boolean {
return _visible;
}
/**
* @private
*/
public function set visible(value:Boolean):void {
if (_visible != value) {
_visible = value;
markToChange();
}
}
/**
* Прозрачность.
*/
public function get alpha():Number {
return _colorTransform.alphaMultiplier;
}
/**
* @private
*/
public function set alpha(value:Number):void {
if (_colorTransform.alphaMultiplier != value) {
_colorTransform.alphaMultiplier = value;
markToChange();
}
}
/**
* Режим наложения.
*/
public function get blendMode():String {
return _blendMode;
}
/**
* @private
*/
public function set blendMode(value:String):void {
if (_blendMode != value) {
_blendMode = value;
markToChange();
}
}
/**
* Трансформация цвета.
*/
public function get colorTransform():ColorTransform {
return new ColorTransform(_colorTransform.redMultiplier, _colorTransform.greenMultiplier, _colorTransform.blueMultiplier, _colorTransform.alphaMultiplier, _colorTransform.redOffset, _colorTransform.greenOffset, _colorTransform.blueOffset, _colorTransform.alphaOffset);
}
/**
* @private
*/
public function set colorTransform(value:ColorTransform):void {
if (value == null) {
throw new TypeError("Parameter colorTransform must be non-null.");
}
if (_colorTransform.redMultiplier != value.redMultiplier || _colorTransform.greenMultiplier != value.greenMultiplier || _colorTransform.blueMultiplier != value.blueMultiplier || _colorTransform.alphaMultiplier != value.alphaMultiplier || _colorTransform.redOffset != value.redOffset || _colorTransform.greenOffset != value.greenOffset || _colorTransform.blueOffset != value.blueOffset || _colorTransform.alphaOffset != value.alphaOffset) {
_colorTransform.redMultiplier = value.redMultiplier;
_colorTransform.greenMultiplier = value.greenMultiplier;
_colorTransform.blueMultiplier = value.blueMultiplier;
_colorTransform.alphaMultiplier = value.alphaMultiplier;
_colorTransform.redOffset = value.redOffset;
_colorTransform.greenOffset = value.greenOffset;
_colorTransform.blueOffset = value.blueOffset;
_colorTransform.alphaOffset = value.alphaOffset;
defaultColorTransform = value.redMultiplier == 1 && value.greenMultiplier == 1 && value.blueMultiplier == 1 && value.redOffset == 0 && value.greenOffset == 0 && value.blueOffset == 0 && value.alphaOffset == 0;
markToChange();
}
}
/**
* Отметить изменение материала.
*/
protected function markToChange():void {}
alternativa3d function draw(item:DisplayItem):void {}
alternativa3d function clear(item:DisplayItem):void {}
/**
* Клонирование объекта. При расширении класса, в наследниках должны быть переопределены вспомогательные
* методы <code>create()</code> и <code>cloneProperties()</code>.
* @return клон объекта
* @see #create()
* @see #cloneProperties()
*/
public function clone():Material {
var res:Material = create();
res.cloneProperties(this);
return res;
}
/**
* Вспомогательный для клонирования метод. Создание нового экземпляра, который выступит в качестве клона после копирования в него свойств.
* Вызывается внутри метода <code>clone()</code>.
* При переопределении метод должен перекрываться полностью.
* @return новый материал
* @see #clone()
*/
protected function create():Material {
return new Material();
}
/**
* Вспомогательный для клонирования метод. Копирование свойств из другого объекта. Вызывается внутри метода <code>clone()</code>
* объектом, который создан с помощью метода <code>create()</code>.
* При переопределении нужно вызывать <code>super.cloneProperties()</code>.
* @param source В качестве источника выступает клонируемый объект
* @see #clone()
* @see #create()
*/
protected function cloneProperties(source:Material):void {
_visible = source._visible;
_blendMode = source._blendMode;
_colorTransform.redMultiplier = source._colorTransform.redMultiplier;
_colorTransform.greenMultiplier = source._colorTransform.greenMultiplier;
_colorTransform.blueMultiplier = source._colorTransform.blueMultiplier;
_colorTransform.alphaMultiplier = source._colorTransform.alphaMultiplier;
_colorTransform.redOffset = source._colorTransform.redOffset;
_colorTransform.greenOffset = source._colorTransform.greenOffset;
_colorTransform.blueOffset = source._colorTransform.blueOffset;
_colorTransform.alphaOffset = source._colorTransform.alphaOffset;
}
}
}

View File

@@ -0,0 +1,66 @@
package alternativa.engine3d.materials {
import alternativa.engine3d.*;
import alternativa.engine3d.core.Space;
import flash.filters.BitmapFilter;
use namespace alternativa3d;
/**
* Базовый класс для материалов пространства.
*/
public class SpaceMaterial extends Material {
/**
* @private
* Пространство
*/
alternativa3d var _space:Space;
/**
* @private
* Фильтры
*/
alternativa3d var _filters:Array = new Array();
/**
* Фильтры.
*/
public function get filters():Array {
return new Array().concat(_filters);
}
/**
* @private
*/
public function set filters(value:Array):void {
var i:uint;
var length:uint;
length = _filters.length;
for (i = 0; i < length; i++) {
_filters.pop();
}
if (value != null) {
length = value.length;
for (i = 0; i < length; i++) {
if (value[i] is BitmapFilter) {
_filters.push(value[i]);
} else {
throw new ArgumentError("Parameter 0 is of the incorrect type. Should be type Filter.");
}
}
}
markToChange();
}
/**
* @inheritDoc
*/
override protected function markToChange():void {
if (_space != null && _space._scene != null) {
_space._scene.spacesToChangeMaterial[_space] = true;
}
}
}
}

View File

@@ -0,0 +1,27 @@
package alternativa.engine3d.materials {
import alternativa.engine3d.*;
import alternativa.engine3d.core.Sprite3D;
use namespace alternativa3d;
/**
* Базовый класс для материалов спрайтов.
*/
public class SpriteMaterial extends Material {
/**
* @private
* Спрайт
*/
alternativa3d var _sprite:Sprite3D;
/**
* @inheritDoc
*/
override protected function markToChange():void {
if (_sprite != null && _sprite._scene != null) {
_sprite._scene.spritesToChangeMaterial[_sprite] = true;
}
}
}
}

View File

@@ -0,0 +1,46 @@
package alternativa.engine3d.materials {
import alternativa.engine3d.*;
import alternativa.engine3d.core.Mesh;
import alternativa.engine3d.core.Scene3D;
import alternativa.engine3d.core.Surface;
import flash.display.BlendMode;
import alternativa.types.Point3D;
use namespace alternativa3d;
/**
* Базовый класс для материалов поверхности.
*/
public class SurfaceMaterial extends Material {
/**
* @private
* Поверхность
*/
alternativa3d var _surface:Surface;
/**
* Поверхность материала.
*/
public function get surface():Surface {
return _surface;
}
/**
* @inheritDoc
*/
override protected function markToChange():void {
if (_surface != null && _surface._mesh != null && _surface._mesh._scene != null) {
_surface._mesh._scene.surfacesToChangeMaterial[_surface] = true;
}
}
/**
* @private
* Отсечение полигона плоскостью.
*/
protected function clip(length:uint, source:Array, target:Array, plane:Point3D, offset:Number):uint {
return 0;
}
}
}

View File

@@ -0,0 +1,49 @@
package alternativa.engine3d.sorting {
import alternativa.engine3d.*;
use namespace alternativa3d;
public class BSPNode extends Node {
// Дочерние полигональные ветки
alternativa3d var frontBSP:BSPNode;
alternativa3d var backBSP:BSPNode;
// Дочерние точечные ноды
alternativa3d var frontDistance:DistanceNode;
alternativa3d var backDistance:DistanceNode;
// Хранилище неиспользуемых нод
static private var collector:Array = new Array();
static alternativa3d function create():BSPNode {
var node:BSPNode;
if ((node = collector.pop()) == null) {
// Если коллектор пуст, создаём новую ноду
return new BSPNode();
}
return node;
}
static alternativa3d function destroy(node:BSPNode):void {
// Удаляем ссылку на уровень
node.sortingLevel = null;
// Отправляем ноду в коллектор
collector.push(node);
}
/*
override alternativa3d function addBSPPrimitive(primitive:FaceBSPPrimitive):void {
}
override alternativa3d function addDistancePrimitive(primitive:DistancePrimitive):void {
}
alternativa3d function removePrimitive(primitive:FaceBSPPrimitive):void {
}
*/
}
}

View File

@@ -0,0 +1,74 @@
package alternativa.engine3d.sorting {
import alternativa.engine3d.*;
import alternativa.types.Set;
import alternativa.engine3d.core.Space;
use namespace alternativa3d;
public class DistanceNode extends Node {
// Список пространств
alternativa3d var spaces:Set = new Set();
// Список спрайтов
alternativa3d var sprites:Set = new Set();
// Список граней
alternativa3d var faces:Set = new Set();
// Хранилище неиспользуемых нод
static private var collector:Array = new Array();
static alternativa3d function create():DistanceNode {
var node:DistanceNode;
if ((node = collector.pop()) == null) {
// Если коллектор пуст, создаём новую ноду
return new DistanceNode();
}
return node;
}
static alternativa3d function destroy(node:DistanceNode):void {
// Удаляем ссылку на уровень
node.sortingLevel = null;
// Отправляем ноду в коллектор
collector.push(node);
}
/*
override alternativa3d function addDistancePrimitive(primitive:DistancePrimitive):void {
// Пометка в уровне об изменении примитива
sortingLevel.changedPrimitives[primitive] = true;
// Устанавливаем связь примитива и ноды
primitives[primitive] = true;
primitive.node = this;
}
*/
alternativa3d function removeSpace(space:Space):void {
trace("removeSpace", sortingLevel, space);
// Пометка в уровне об изменении примитива
sortingLevel.changed[space] = true;
// Удаляем связь пространства и ноды
space.node = null;
delete spaces[space];
// Если в ноде примитивов больше нет
if (spaces.isEmpty() && sprites.isEmpty() && faces.isEmpty()) {
// Если есть родительская нода
if (parent != null) {
// Удаляем связь ноды с родительской нодой
if (parent.backDistance == this) {
parent.backDistance = null;
} else {
parent.frontDistance = null;
}
parent = null;
} else {
// Удаляем корневую ноду из уровня
sortingLevel.distanceNode = null;
}
// Отправляем ноду в коллектор
destroy(this);
}
}
}
}

View File

@@ -0,0 +1,17 @@
package alternativa.engine3d.sorting {
import alternativa.engine3d.*;
import alternativa.types.Point3D;
use namespace alternativa3d;
public class DistancePrimitive extends Primitive {
// Нода
alternativa3d var node:DistanceNode;
// Координаты в пространстве
alternativa3d var coords:Point3D = new Point3D();
}
}

View File

@@ -0,0 +1,47 @@
package alternativa.engine3d.sorting {
import alternativa.engine3d.*;
import alternativa.engine3d.core.Face;
use namespace alternativa3d;
public class FaceBSPPrimitive extends Primitive {
// Ссылка на грань
alternativa3d var face:Face;
// Нода
alternativa3d var node:BSPNode;
// Уровень в BSP
alternativa3d var bspLevel:int;
// Хранилище неиспользуемых примитивов
static private var collector:Array = new Array();
// Примитивы на отложенное удаление
static private var deferred:Array = new Array();
static alternativa3d function create():FaceBSPPrimitive {
var primitive:FaceBSPPrimitive;
if ((primitive = collector.pop()) == null) {
// Если коллектор пуст, создаём новый примитив
return new FaceBSPPrimitive();
}
return primitive;
}
static alternativa3d function defer(primitive:FaceBSPPrimitive):void {
deferred.push(primitive);
}
static alternativa3d function destroyDeferred():void {
var primitive:FaceBSPPrimitive;
while ((primitive = deferred.pop()) != null) {
primitive.face = null;
primitive.node = null;
collector.push(primitive);
}
}
}
}

View File

@@ -0,0 +1,42 @@
package alternativa.engine3d.sorting {
import alternativa.engine3d.*;
import alternativa.engine3d.core.Face;
use namespace alternativa3d;
public class FaceDistancePrimitive extends DistancePrimitive {
// Ссылка на грань
alternativa3d var face:Face;
// Хранилище неиспользуемых примитивов
static private var collector:Array = new Array();
// Примитивы на отложенное удаление
static private var deferred:Array = new Array();
static alternativa3d function create():FaceDistancePrimitive {
var primitive:FaceDistancePrimitive;
if ((primitive = collector.pop()) == null) {
// Если коллектор пуст, создаём новый примитив
return new FaceDistancePrimitive();
}
return primitive;
}
static alternativa3d function defer(primitive:FaceDistancePrimitive):void {
deferred.push(primitive);
}
static alternativa3d function destroyDeferred():void {
var primitive:FaceDistancePrimitive;
while ((primitive = deferred.pop()) != null) {
primitive.face = null;
primitive.node = null;
collector.push(primitive);
}
}
}
}

View File

@@ -0,0 +1,41 @@
package alternativa.engine3d.sorting {
import alternativa.engine3d.*;
import alternativa.engine3d.core.Face;
use namespace alternativa3d;
public class FaceNonePrimitive extends NonePrimitive {
// Ссылка на грань
alternativa3d var face:Face;
// Хранилище неиспользуемых примитивов
static private var collector:Array = new Array();
// Примитивы на отложенное удаление
static private var deferred:Array = new Array();
static alternativa3d function create():FaceNonePrimitive {
var primitive:FaceNonePrimitive;
if ((primitive = collector.pop()) == null) {
// Если коллектор пуст, создаём новый примитив
return new FaceNonePrimitive();
}
return primitive;
}
static alternativa3d function defer(primitive:FaceNonePrimitive):void {
deferred.push(primitive);
}
static alternativa3d function destroyDeferred():void {
var primitive:FaceNonePrimitive;
while ((primitive = deferred.pop()) != null) {
primitive.face = null;
primitive.sortingLevel = null;
collector.push(primitive);
}
}
}
}

View File

@@ -0,0 +1,17 @@
package alternativa.engine3d.sorting {
import alternativa.engine3d.*;
import alternativa.engine3d.core.Space;
use namespace alternativa3d;
public class Node {
// Уровень
alternativa3d var sortingLevel:SortingLevel;
// Родительская нода
alternativa3d var parent:BSPNode;
}
}

View File

@@ -0,0 +1,13 @@
package alternativa.engine3d.sorting {
import alternativa.engine3d.*;
use namespace alternativa3d;
public class NonePrimitive extends Primitive {
// Уровень сортировки
alternativa3d var sortingLevel:SortingLevel;
}
}

View File

@@ -0,0 +1,8 @@
package alternativa.engine3d.sorting {
import alternativa.engine3d.*;
use namespace alternativa3d;
public class Primitive {}
}

View File

@@ -0,0 +1,86 @@
package alternativa.engine3d.sorting {
import alternativa.engine3d.*;
import alternativa.engine3d.core.Space;
import alternativa.types.Set;
use namespace alternativa3d;
public class SortingLevel {
// Порядковый номер
alternativa3d var index:int;
// Пространство
alternativa3d var space:Space;
// Следующий уровень
alternativa3d var next:SortingLevel;
// Корневая нода дерева
alternativa3d var bspNode:BSPNode;
alternativa3d var distanceNode:DistanceNode;
// Список несортируемых пространств
alternativa3d var spaces:Set = new Set();
// Список пространств на сортировку по дистанции
alternativa3d var spacesDistance:Set = new Set();
// Список несортируемых спрайтов
alternativa3d var sprites:Set = new Set();
// Список спрайтов на сортировку по дистанции
alternativa3d var spritesDistance:Set = new Set();
// Список несортируемых граней
alternativa3d var faces:Set = new Set();
// Список граней на сортировку по дистанции
alternativa3d var facesDistance:Set = new Set();
// Список граней на сортировку по BSP
alternativa3d var facesBSP:Array = new Array();
/**
* @private
* Список изменённых объектов
*/
alternativa3d var changed:Set = new Set();
alternativa3d function calculate():void {
/*var polyPrimitive:FacePolyPrimitive;
var pointPrimitive:PointPrimitive;
// Добавляем полигональные примитивы
while ((polyPrimitive = polyPrimitivesToAdd.pop()) != null) {
}
// Если есть точечные примитивы на добавление
if (pointPrimitivesToAdd[0] != undefined) {
// Если корневого нода ещё нет, создаём
if (root == null) {
root = PointNode.create();
root.space = this;
}
// Встраиваем примитивы в дерево
while ((pointPrimitive = pointPrimitivesToAdd.pop()) != null) {
trace(pointPrimitive);
root.addPointPrimitive(pointPrimitive);
}
}*/
// Если есть изменения, помечаем на очистку
if (!changed.isEmpty()) {
space._scene.levelsToClear[this] = true;
}
delete space._scene.levelsToCalculate[this];
}
alternativa3d function clear():void {
trace(this, "clear");
delete space._scene.levelsToClear[this];
}
}
}

View File

@@ -0,0 +1,10 @@
package alternativa.engine3d.sorting {
public class SortingMode {
public static const NONE:uint = 0;
public static const DISTANCE:uint = 1;
public static const BSP:uint = 2;
}
}

View File

@@ -0,0 +1,42 @@
package alternativa.engine3d.sorting {
import alternativa.engine3d.*;
import alternativa.engine3d.core.Space;
use namespace alternativa3d;
public class SpaceDistancePrimitive extends DistancePrimitive {
// Ссылка на пространство
alternativa3d var space:Space;
// Хранилище неиспользуемых примитивов
static private var collector:Array = new Array();
// Примитивы на отложенное удаление
static private var deferred:Array = new Array();
static alternativa3d function create():SpaceDistancePrimitive {
var primitive:SpaceDistancePrimitive;
if ((primitive = collector.pop()) == null) {
// Если коллектор пуст, создаём новый примитив
return new SpaceDistancePrimitive();
}
return primitive;
}
static alternativa3d function defer(primitive:SpaceDistancePrimitive):void {
deferred.push(primitive);
}
static alternativa3d function destroyDeferred():void {
var primitive:SpaceDistancePrimitive;
while ((primitive = deferred.pop()) != null) {
primitive.space = null;
primitive.node = null;
collector.push(primitive);
}
}
}
}

View File

@@ -0,0 +1,41 @@
package alternativa.engine3d.sorting {
import alternativa.engine3d.*;
import alternativa.engine3d.core.Space;
use namespace alternativa3d;
public class SpaceNonePrimitive extends NonePrimitive {
// Ссылка на пространство
alternativa3d var space:Space;
// Хранилище неиспользуемых примитивов
static private var collector:Array = new Array();
// Примитивы на отложенное удаление
static private var deferred:Array = new Array();
static alternativa3d function create():SpaceNonePrimitive {
var primitive:SpaceNonePrimitive;
if ((primitive = collector.pop()) == null) {
// Если коллектор пуст, создаём новый примитив
return new SpaceNonePrimitive();
}
return primitive;
}
static alternativa3d function defer(primitive:SpaceNonePrimitive):void {
deferred.push(primitive);
}
static alternativa3d function destroyDeferred():void {
var primitive:SpaceNonePrimitive;
while ((primitive = deferred.pop()) != null) {
primitive.space = null;
primitive.sortingLevel = null;
collector.push(primitive);
}
}
}
}

View File

@@ -0,0 +1,42 @@
package alternativa.engine3d.sorting {
import alternativa.engine3d.*;
import alternativa.engine3d.core.Sprite3D;
use namespace alternativa3d;
public class SpriteDistancePrimitive extends DistancePrimitive {
// Ссылка на спрайт
alternativa3d var sprite:Sprite3D;
// Хранилище неиспользуемых примитивов
static private var collector:Array = new Array();
// Примитивы на отложенное удаление
static private var deferred:Array = new Array();
static alternativa3d function create():SpriteDistancePrimitive {
var primitive:SpriteDistancePrimitive;
if ((primitive = collector.pop()) == null) {
// Если коллектор пуст, создаём новый примитив
return new SpriteDistancePrimitive();
}
return primitive;
}
static alternativa3d function defer(primitive:SpriteDistancePrimitive):void {
deferred.push(primitive);
}
static alternativa3d function destroyDeferred():void {
var primitive:SpriteDistancePrimitive;
while ((primitive = deferred.pop()) != null) {
primitive.sprite = null;
primitive.node = null;
collector.push(primitive);
}
}
}
}

View File

@@ -0,0 +1,41 @@
package alternativa.engine3d.sorting {
import alternativa.engine3d.*;
import alternativa.engine3d.core.Sprite3D;
use namespace alternativa3d;
public class SpriteNonePrimitive extends NonePrimitive {
// Ссылка на спрайт
alternativa3d var sprite:Sprite3D;
// Хранилище неиспользуемых примитивов
static private var collector:Array = new Array();
// Примитивы на отложенное удаление
static private var deferred:Array = new Array();
static alternativa3d function create():SpriteNonePrimitive {
var primitive:SpriteNonePrimitive;
if ((primitive = collector.pop()) == null) {
// Если коллектор пуст, создаём новый примитив
return new SpriteNonePrimitive();
}
return primitive;
}
static alternativa3d function defer(primitive:SpriteNonePrimitive):void {
deferred.push(primitive);
}
static alternativa3d function destroyDeferred():void {
var primitive:SpriteNonePrimitive;
while ((primitive = deferred.pop()) != null) {
primitive.sprite = null;
primitive.sortingLevel = null;
collector.push(primitive);
}
}
}
}