f = d/tan(fov/2), где d является половиной диагонали поля вывода.
+ * Угол зрения ограничен диапазоном 0-180 градусов.
+ */
+ public function get fov():Number {
+ return _fov;
+ }
+
+ /**
+ * @private
+ */
+ public function set fov(value:Number):void {
+ value = (value < 0) ? 0 : ((value > (Math.PI - 0.0001)) ? (Math.PI - 0.0001) : value);
+ if (_fov != value) {
+ // Если перспектива
+ if (!_orthographic) {
+ // Отправляем сигнал об изменении плоскостей отсечения
+ // ...
+ }
+ // Сохраняем новое значение
+ _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;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/core/Face.as b/Alternativa3D6/6.0/alternativa/engine3d/core/Face.as
new file mode 100644
index 0000000..345a1b6
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/core/Face.as
@@ -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];
+ }
+
+ /**
+ * Массив вершин грани, представленных объектами класса alternativa.engine3d.core.Vertex.
+ *
+ * @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;
+ }
+
+ /**
+ * Идентификатор грани в полигональном объекте. В случае, если грань не принадлежит ни одному объекту, идентификатор
+ * имеет значение null.
+ */
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/core/Mesh.as b/Alternativa3D6/6.0/alternativa/engine3d/core/Mesh.as
new file mode 100644
index 0000000..7bf541e
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/core/Mesh.as
@@ -0,0 +1,1084 @@
+package alternativa.engine3d.core {
+ import alternativa.engine3d.*;
+ import alternativa.engine3d.errors.FaceExistsError;
+ import alternativa.engine3d.errors.FaceNeedMoreVerticesError;
+ import alternativa.engine3d.errors.FaceNotFoundError;
+ import alternativa.engine3d.errors.InvalidIDError;
+ import alternativa.engine3d.errors.SurfaceExistsError;
+ import alternativa.engine3d.errors.SurfaceNotFoundError;
+ import alternativa.engine3d.errors.VertexExistsError;
+ import alternativa.engine3d.errors.VertexNotFoundError;
+ import alternativa.types.Map;
+ import alternativa.utils.ObjectUtils;
+
+ use namespace alternativa3d;
+
+ /**
+ * Полигональный объект — базовый класс для трёхмерных объектов, состоящих из граней-полигонов. Объект
+ * содержит в себе наборы вершин, граней и поверхностей.
+ */
+ public class Mesh extends Object3D {
+
+ // Инкремент количества объектов
+ private static var counter:uint = 0;
+
+ // Инкременты для идентификаторов вершин, граней и поверхностей
+ private var vertexIDCounter:uint = 0;
+ private var faceIDCounter:uint = 0;
+ private var surfaceIDCounter:uint = 0;
+
+ /**
+ * @private
+ * Список вершин
+ */
+ alternativa3d var _vertices:Map = new Map();
+ /**
+ * @private
+ * Список граней
+ */
+ alternativa3d var _faces:Map = new Map();
+ /**
+ * @private
+ * Список поверхностей
+ */
+ alternativa3d var _surfaces:Map = new Map();
+
+ /**
+ * Создание экземпляра полигонального объекта.
+ *
+ * @param name имя экземпляра
+ */
+ public function Mesh(name:String = null) {
+ super(name);
+ }
+
+ override protected function transform():void {
+ super.transform();
+
+ // Перемещаем вершины
+ for each (var vertex:Vertex in _vertices) {
+ vertex.move();
+ }
+
+ // Трансформируем поверхности
+ for each (var surface:Surface in _surfaces) {
+ // Если у поверхности есть материал
+ if (surface._material != null) {
+ var key:*;
+ var face:Face;
+
+ // Если полигональная сортировка
+ if (surface._sortingMode == 0) {
+ // Обрабатываем грани поверхности
+ for (key in surface._faces) {
+ face = key;
+ // Если есть полигональный примитив
+ if (face.polyPrimitive != null) {
+ // Расчитываем перпендикуляр грани
+ face.calculatePerpendicular();
+ // Обновление полигонального примитива
+ face.updatePolyPrimitive();
+ // Снимаем пометку на трансформацию
+ delete _scene.facesToTransform[face];
+ } else {
+ // Если есть точечный примитив
+ if (face.pointPrimitive != null) {
+ // Расчитываем перпендикуляр грани
+ face.calculatePerpendicular();
+ // Смена типа примитива с точечного на полигональный
+ face.changePointToPolyPrimitive();
+ // Снимаем пометку на трансформацию
+ delete _scene.facesToTransform[face];
+ } else {
+ // Расчитываем перпендикуляр грани
+ face.calculatePerpendicular();
+ // Создание полигонального примитива
+ face.createPolyPrimitive();
+ }
+ }
+ // Снимаем пометку грани на смену поверхности
+ delete _scene.facesToChangeSurface[face];
+ }
+ } else {
+ // Если точечная сортировка
+ if (surface._sortingMode == 1) {
+ // Обрабатываем грани поверхности
+ for (key in surface._faces) {
+ face = key;
+ // Если есть точечный примитив
+ if (face.pointPrimitive != null) {
+ // Расчитываем перпендикуляр грани
+ face.calculatePerpendicular();
+ // Обновление точечного примитива
+ face.updatePointPrimitive();
+ // Снимаем пометку на трансформацию
+ delete _scene.facesToTransform[face];
+ } else {
+ // Если есть полигональный примитив
+ if (face.polyPrimitive != null) {
+ // Расчитываем перпендикуляр грани
+ face.calculatePerpendicular();
+ // Смена типа примитива с полигонального на точечный
+ face.changePolyToPointPrimitive();
+ // Снимаем пометку на трансформацию
+ delete _scene.facesToTransform[face];
+ } else {
+ // Расчитываем перпендикуляр грани
+ face.calculatePerpendicular();
+ // Создание точечного примитива
+ face.createPointPrimitive();
+ }
+ }
+ // Снимаем пометку грани на смену поверхности
+ delete _scene.facesToChangeSurface[face];
+ }
+ } else {
+ // Если нет сортировки
+
+ }
+ }
+ // Снимаем все пометки поверхности
+ delete _scene.surfacesToChangeMaterial[surface];
+ delete _scene.surfacesToChangeSortingMode[surface];
+ delete _scene.surfacesToChangeBSPLevel[surface];
+ }
+ }
+ }
+
+ override protected function move():void {
+ super.move();
+
+ // Перемещаем вершины
+ for each (var vertex:Vertex in _vertices) {
+ vertex.move();
+ }
+
+ // Перемещаем поверхности
+ for each (var surface:Surface in _surfaces) {
+ // Если у поверхности есть материал
+ if (surface._material != null) {
+ var key:*;
+ var face:Face;
+
+ // Если полигональная сортировка
+ if (surface._sortingMode == 0) {
+ // Обрабатываем грани поверхности
+ for (key in surface._faces) {
+ face = key;
+ // Если есть полигональный примитив
+ if (face.polyPrimitive != null) {
+ // Если грань помечена на трансформацию
+ if (_scene.facesToTransform[face]) {
+ // Расчитываем перпендикуляр грани
+ face.calculatePerpendicular();
+ // Снимаем пометку на трансформацию
+ delete _scene.facesToTransform[face];
+ }
+ // Обновление полигонального примитива
+ face.updatePolyPrimitive();
+ } else {
+ // Если есть точечный примитив
+ if (face.pointPrimitive != null) {
+ // Если грань помечена на трансформацию
+ if (_scene.facesToTransform[face]) {
+ // Расчитываем перпендикуляр грани
+ face.calculatePerpendicular();
+ // Снимаем пометку на трансформацию
+ delete _scene.facesToTransform[face];
+ }
+ // Смена типа примитива с точечного на полигональный
+ face.changePointToPolyPrimitive();
+ } else {
+ // Расчитываем перпендикуляр грани
+ face.calculatePerpendicular();
+ // Создание полигонального примитива
+ face.createPolyPrimitive();
+ }
+ }
+ // Снимаем пометку грани на смену поверхности
+ delete _scene.facesToChangeSurface[face];
+ }
+ } else {
+ // Если точечная сортировка
+ if (surface._sortingMode == 1) {
+ // Обрабатываем грани поверхности
+ for (key in surface._faces) {
+ face = key;
+ // Если есть точечный примитив
+ if (face.pointPrimitive != null) {
+ // Если грань помечена на трансформацию
+ if (_scene.facesToTransform[face]) {
+ // Расчитываем перпендикуляр грани
+ face.calculatePerpendicular();
+ // Снимаем пометку на трансформацию
+ delete _scene.facesToTransform[face];
+ }
+ // Обновление точечного примитива
+ face.updatePointPrimitive();
+ } else {
+ // Если есть полигональный примитив
+ if (face.polyPrimitive != null) {
+ // Если грань помечена на трансформацию
+ if (_scene.facesToTransform[face]) {
+ // Расчитываем перпендикуляр грани
+ face.calculatePerpendicular();
+ // Снимаем пометку на трансформацию
+ delete _scene.facesToTransform[face];
+ }
+ // Смена типа примитива с полигонального на точечный
+ face.changePolyToPointPrimitive();
+ } else {
+ // Расчитываем перпендикуляр грани
+ face.calculatePerpendicular();
+ // Создание точечного примитива
+ face.createPointPrimitive();
+ }
+ }
+ // Снимаем пометку грани на смену поверхности
+ delete _scene.facesToChangeSurface[face];
+ }
+ } else {
+ // Если нет сортировки
+
+ }
+ }
+ // Снимаем все пометки поверхности
+ delete _scene.surfacesToChangeMaterial[surface];
+ delete _scene.surfacesToChangeSortingMode[surface];
+ delete _scene.surfacesToChangeBSPLevel[surface];
+ }
+ }
+ }
+
+ override protected function removeFromScene(scene:Scene3D):void {
+ super.removeFromScene(scene);
+
+ // Удаляем все пометки вершин
+ for each (var vertex:Vertex in _vertices) {
+ delete scene.verticesToMove[vertex];
+ }
+
+ // Удаляем все пометки граней
+ for each (var face:Face in _faces) {
+ // Если у грани есть точечный примитив
+ if (face.pointPrimitive != null) {
+ // Удаляем точечный примитив
+ face.destroyPointPrimitive();
+ } else {
+ // Если у грани есть полигональный примитив
+ if (face.polyPrimitive != null) {
+ // Удаляем полигональный примитив
+ face.destroyPolyPrimitive();
+ }
+ }
+ delete scene.facesToChangeSurface[face];
+ delete scene.facesToTransform[face];
+ }
+
+ // Удаляем все пометки поверхностей
+ for each (var surface:Surface in _surfaces) {
+ delete scene.surfacesToChangeSortingMode[surface];
+ delete scene.surfacesToChangeMaterial[surface];
+ delete scene.surfacesToChangeBSPLevel[surface];
+ }
+ }
+
+ /**
+ * Добавление новой вершины к объекту.
+ *
+ * @param x координата X в локальной системе координат объекта
+ * @param y координата Y в локальной системе координат объекта
+ * @param z координата Z в локальной системе координат объекта
+ * @param id идентификатор вершины. Если указано значение null, идентификатор будет
+ * сформирован автоматически.
+ *
+ * @return экземпляр добавленной вершины
+ *
+ * @throws alternativa.engine3d.errors.VertexExistsError объект уже содержит вершину с указанным идентификатором
+ * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
+ */
+ public function createVertex(x:Number = 0, y:Number = 0, z:Number = 0, id:Object = null):Vertex {
+ // Проверяем ID
+ if (id != null) {
+ // Если уже есть вершина с таким ID
+ if (_vertices[id] != undefined) {
+ if (_vertices[id] is Vertex) {
+ throw new VertexExistsError(id, this);
+ } else {
+ // ID некорректный
+ throw new InvalidIDError(id, this);
+ }
+ }
+ } else {
+ // Ищем первый свободный
+ while (_vertices[vertexIDCounter] != undefined) {
+ vertexIDCounter++;
+ }
+ id = vertexIDCounter;
+ }
+
+ // Создаём вершину
+ var v:Vertex = new Vertex();
+ v._coords.x = x;
+ v._coords.y = y;
+ v._coords.z = z;
+
+ // Добавляем вершину в меш
+ _vertices[id] = v;
+ // Указываем меш вершине
+ v._mesh = this;
+
+ // Помечаем вершину на перемещение
+ if (_scene != null) {
+ _scene.verticesToMove[v] = true;
+ }
+
+ return v;
+ }
+
+ /**
+ * Удаление вершины из объекта. При удалении вершины из объекта также удаляются все грани, которым принадлежит данная вершина.
+ *
+ * @param vertex экземпляр класса alternativa.engine3d.core.Vertex или идентификатор удаляемой вершины
+ *
+ * @return экземпляр удалённой вершины
+ *
+ * @throws alternativa.engine3d.errors.VertexNotFoundError объект не содержит указанную вершину
+ * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
+ */
+ public function removeVertex(vertex:Object):Vertex {
+ var byLink:Boolean = vertex is Vertex;
+
+ // Проверяем на null
+ if (vertex == null) {
+ throw new VertexNotFoundError(null, this);
+ }
+
+ // Проверяем наличие вершины в меше
+ if (byLink) {
+ // Если удаляем по ссылке
+ if (Vertex(vertex)._mesh != this) {
+ // Если вершина не в меше
+ throw new VertexNotFoundError(vertex, this);
+ }
+ } else {
+ // Если удаляем по ID
+ if (_vertices[vertex] == undefined) {
+ // Если нет вершины с таким ID
+ throw new VertexNotFoundError(vertex, this);
+ } else {
+ if (!(_vertices[vertex] is Vertex)) {
+ // ID некорректный
+ throw new InvalidIDError(vertex, this);
+ }
+ }
+ }
+
+ // Находим вершину и её ID
+ var v:Vertex = byLink ? Vertex(vertex) : _vertices[vertex];
+ var id:Object = byLink ? getVertexId(Vertex(vertex)) : vertex;
+
+ // Удаляем все пометки вершины в сцене
+ if (_scene != null) {
+ delete _scene.verticesToMove[v];
+ }
+
+ // Удаляем зависимые грани
+ for (var key:* in v._faces) {
+ removeFace(key);
+ delete v._faces[key];
+ }
+
+ // Удаляем вершину из меша
+ delete _vertices[id];
+ // Удаляем ссылку на меш в вершине
+ v._mesh = null;
+
+ return v;
+ }
+
+ /**
+ * Добавление грани к объекту. В результате выполнения метода в объекте появляется новая грань, не привязанная
+ * ни к одной поверхности.
+ *
+ * @param vertices массив вершин грани, указанных в порядке обхода лицевой стороны грани против часовой
+ * стрелки. Каждый элемент массива может быть либо экземпляром класса alternativa.engine3d.core.Vertex,
+ * либо идентификатором в наборе вершин объекта. В обоих случаях объект должен содержать указанную вершину.
+ * @param id идентификатор грани. Если указано значение null, идентификатор будет
+ * сформирован автоматически.
+ *
+ * @return экземпляр добавленной грани
+ *
+ * @throws alternativa.engine3d.errors.FaceNeedMoreVerticesError в качестве массива вершин был передан
+ * null, либо количество вершин в массиве меньше трёх
+ * @throws alternativa.engine3d.errors.FaceExistsError объект уже содержит грань с заданным идентификатором
+ * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
+ * @throws alternativa.engine3d.errors.VertexNotFoundError объект не содержит какую-либо вершину из входного массива
+ *
+ * @see Vertex
+ */
+ public function createFace(vertices:Array, id:Object = null):Face {
+
+ // Проверяем на null
+ if (vertices == null) {
+ throw new FaceNeedMoreVerticesError(this);
+ }
+
+ // Проверяем ID
+ if (id != null) {
+ // Если уже есть грань с таким ID
+ if (_faces[id] != undefined) {
+ if (_faces[id] is Face) {
+ throw new FaceExistsError(id, this);
+ } else {
+ // ID некорректный
+ throw new InvalidIDError(id, this);
+ }
+ }
+ } else {
+ // Ищем первый свободный ID
+ while (_faces[faceIDCounter] != undefined) {
+ faceIDCounter++;
+ }
+ id = faceIDCounter;
+ }
+
+ // Проверяем количество точек
+ var length:uint = vertices.length;
+ if (length < 3) {
+ throw new FaceNeedMoreVerticesError(this, length);
+ }
+
+ // Создаём грань
+ var f:Face = new Face();
+
+ // Добавляем грань в меш
+ _faces[id] = f;
+ // Указываем меш грани
+ f._mesh = this;
+
+ // Проверяем и формируем список вершин
+ f._verticesCount = length;
+ var v:Array = f._vertices;
+ var vertex:Vertex;
+ for (var i:uint = 0; i < length; i++) {
+ if (vertices[i] is Vertex) {
+ // Если работаем со ссылками
+ vertex = vertices[i];
+ if (vertex._mesh != this) {
+ // Если вершина не в меше
+ throw new VertexNotFoundError(vertices[i], this);
+ }
+ } else {
+ // Если работаем с ID
+ if (_vertices[vertices[i]] == null) {
+ // Если нет вершины с таким ID
+ throw new VertexNotFoundError(vertices[i], this);
+ } else {
+ if (!(_vertices[vertices[i]] is Vertex)) {
+ // ID некорректный
+ throw new InvalidIDError(vertices[i],this);
+ }
+ }
+ vertex = _vertices[vertices[i]];
+ }
+ // Добавляем вершину в список грани
+ v.push(vertex);
+ // Указываем грань вершине
+ vertex._faces[f] = true;
+ }
+
+ return f;
+ }
+
+ /**
+ * Удаление грани из объекта. Грань также удаляется из поверхности объекта, которой она принадлежит.
+ *
+ * @param экземпляр класса alternativa.engine3d.core.Face или идентификатор удаляемой грани
+ *
+ * @return экземпляр удалённой грани
+ *
+ * @throws alternativa.engine3d.errors.FaceNotFoundError объект не содержит указанную грань
+ * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
+ */
+ public function removeFace(face:Object):Face {
+ var byLink:Boolean = face is Face;
+
+ // Проверяем на null
+ if (face == null) {
+ throw new FaceNotFoundError(null, this);
+ }
+
+ // Проверяем наличие грани в меше
+ if (byLink) {
+ // Если удаляем по ссылке
+ if (Face(face)._mesh != this) {
+ // Если грань не в меше
+ throw new FaceNotFoundError(face, this);
+ }
+ } else {
+ // Если удаляем по ID
+ if (_faces[face] == undefined) {
+ // Если нет грани с таким ID
+ throw new FaceNotFoundError(face, this);
+ } else {
+ if (!(_faces[face] is Face)) {
+ // ID некорректный
+ throw new InvalidIDError(face, this);
+ }
+ }
+ }
+
+ // Находим грань и её ID
+ var f:Face = byLink ? Face(face) : _faces[face] ;
+ var id:Object = byLink ? getFaceId(Face(face)) : face;
+
+ // Если в сцене
+ if (_scene != null) {
+ // Удаляем примитив
+ if (f.pointPrimitive != null) {
+ // Удаляем точечный примитив
+ f.destroyPointPrimitive();
+ // Помечаем пространство на пересчёт
+ _scene.spacesToCalculate[space] = true;
+ } else {
+ // Если у грани есть полигональный примитив
+ if (f.polyPrimitive != null) {
+ // Удаляем полигональный примитив
+ f.destroyPolyPrimitive();
+ // Помечаем пространство на пересчёт
+ _scene.spacesToCalculate[space] = true;
+ }
+ }
+
+ // Удаляем все пометки грани в сцене
+ delete _scene.facesToChangeSurface[f];
+ delete _scene.facesToTransform[f];
+ }
+
+ // Удаляем грань из поверхности
+ if (f._surface != null) {
+ delete f._surface._faces[f];
+ f._surface = null;
+ }
+
+ // Удаляем вершины из грани
+ for (var i:uint = 0; i < f._verticesCount; i++) {
+ var vertex:Vertex = f._vertices.pop();
+ delete vertex._faces[f];
+ }
+ f._verticesCount = 0;
+
+ // Удаляем грань из меша
+ delete _faces[id];
+ // Удаляем ссылку на меш в грани
+ f._mesh = null;
+
+ return f;
+ }
+
+ /**
+ * Добавление новой поверхности к объекту.
+ *
+ * @param faces набор граней, составляющих поверхность. Каждый элемент массива должен быть либо экземпляром класса
+ * alternativa.engine3d.core.Face, либо идентификатором грани. В обоих случаях объект должен содержать
+ * указанную грань. Если значение параметра равно null, то будет создана пустая поверхность. Если
+ * какая-либо грань содержится в другой поверхности, она будет перенесена в новую поверхность.
+ * @param id идентификатор новой поверхности. Если указано значение null, идентификатор будет
+ * сформирован автоматически.
+ *
+ * @return экземпляр добавленной поверхности
+ *
+ * @throws alternativa.engine3d.errors.SurfaceExistsError объект уже содержит поверхность с заданным идентификатором
+ * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
+ *
+ * @see Face
+ */
+ public function createSurface(faces:Array = null, id:Object = null):Surface {
+ // Проверяем ID
+ if (id != null) {
+ // Если уже есть поверхность с таким ID
+ if (_surfaces[id] != undefined) {
+ if (_surfaces[id] is Surface) {
+ throw new SurfaceExistsError(id, this);
+ } else {
+ // ID некорректный
+ throw new InvalidIDError(id, this);
+ }
+ }
+ } else {
+ // Ищем первый свободный ID
+ while (_surfaces[surfaceIDCounter] != undefined) {
+ surfaceIDCounter++;
+ }
+ id = surfaceIDCounter;
+ }
+
+ // Создаём поверхность
+ var s:Surface = new Surface();
+
+ // Добавляем поверхность в меш
+ _surfaces[id] = s;
+ // Указываем меш поверхности
+ s._mesh = this;
+
+ // Добавляем грани, если есть
+ if (faces != null) {
+ var length:uint = faces.length;
+ for (var i:uint = 0; i < length; i++) {
+ s.addFace(faces[i]);
+ }
+ }
+
+ return s;
+ }
+
+ /**
+ * Удаление поверхности объекта. Из удаляемой поверхности также удаляются все содержащиеся в ней грани.
+ *
+ * @param surface экземпляр класса alternativa.engine3d.core.Face или идентификатор удаляемой поверхности
+ *
+ * @return экземпляр удалённой поверхности
+ *
+ * @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит указанную поверхность
+ * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
+ */
+ public function removeSurface(surface:Object):Surface {
+ var byLink:Boolean = surface is Surface;
+
+ // Проверяем на null
+ if (surface == null) {
+ throw new SurfaceNotFoundError(null, this);
+ }
+
+ // Проверяем наличие поверхности в меше
+ if (byLink) {
+ // Если удаляем по ссылке
+ if (Surface(surface)._mesh != this) {
+ // Если поверхность не в меше
+ throw new SurfaceNotFoundError(surface, this);
+ }
+ } else {
+ // Если удаляем по ID
+ if (_surfaces[surface] == undefined) {
+ // Если нет поверхности с таким ID
+ throw new SurfaceNotFoundError(surface, this);
+ } else {
+ if (!(_surfaces[surface] is Surface)) {
+ // ID некорректный
+ throw new InvalidIDError(surface, this);
+ }
+ }
+ }
+
+ // Находим поверхность и её ID
+ var s:Surface = byLink ? Surface(surface) : _surfaces[surface];
+ var id:Object = byLink ? getSurfaceId(Surface(surface)) : surface;
+
+
+ var key:*;
+ var face:Face;
+ if (_scene != null) {
+ // Удаляем грани из поверхности и помечаем их на смену поверхности
+ for (key in s._faces) {
+ face = key;
+ _scene.facesToChangeSurface[face] = true;
+ delete s._faces[face];
+ face._surface = null;
+ }
+ // Удаляем все пометки поверхности в сцене
+ delete _scene.surfacesToChangeSortingLevel[s];
+ delete _scene.surfacesToChangeSortingMode[s];
+ delete _scene.surfacesToChangeMaterial[s];
+ delete _scene.surfacesToChangeBSPLevel[s];
+ } else {
+ // Удаляем грани из поверхности
+ for (key in s._faces) {
+ face = key;
+ delete s._faces[face];
+ face._surface = null;
+ }
+ }
+
+ // Удаляем поверхность из меша
+ delete _surfaces[id];
+ // Удаляем ссылку на меш в поверхности
+ s._mesh = null;
+
+ return s;
+ }
+
+ /**
+ * Добавление всех граней объекта в указанную поверхность.
+ *
+ * @param surface экземпляр класса alternativa.engine3d.core.Surface или идентификатор поверхности, в
+ * которую добавляются грани. Если задан идентификатор, и объект не содержит поверхность с таким идентификатором,
+ * будет создана новая поверхность.
+ *
+ * @param removeSurfaces удалять или нет пустые поверхности после переноса граней
+ *
+ * @return экземпляр поверхности, в которую перенесены грани
+ *
+ * @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит указанный экземпляр поверхности
+ * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
+ */
+ public function moveAllFacesToSurface(surface:Object = null, removeSurfaces:Boolean = false):Surface {
+ var byLink:Boolean = surface is Surface;
+
+ if (byLink) {
+ // Если работаем по ссылке
+ if (Surface(surface)._mesh != this) {
+ // Если поверхность не в меше
+ throw new SurfaceNotFoundError(surface, this);
+ }
+ } else {
+ // Если работаем по ID
+ if (surface != null) {
+ // Если ID задан
+ if (_surfaces[surface] == undefined) {
+ // Если нет поверхности с таким ID
+ throw new SurfaceNotFoundError(surface, this);
+ } else {
+ if (!(_surfaces[surface] is Surface)) {
+ // ID некорректный
+ throw new InvalidIDError(surface, this);
+ }
+ }
+ }
+ }
+
+ // Находим поверхность и её ID
+ var s:Surface = byLink ? Surface(surface) : ((surface != null) ? _surfaces[surface] : createSurface(null, surface));
+ var id:Object = byLink ? getSurfaceId(Surface(surface)) : ((surface != null) ? surface : surfaceIDCounter);
+
+ // Перемещаем грани в поверхность
+ for each (var face:Face in _faces) {
+ if (face._surface != s) {
+ s.addFace(face);
+ }
+ }
+
+ if (removeSurfaces) {
+ // Удаляем оставшиеся поверхности
+ for (var key:* in _surfaces) {
+ if (key != id) {
+ _surfaces[key]._mesh = null;
+ delete _surfaces[key];
+
+ // Удаляем все пометки поверхности в сцене
+ if (_scene != null) {
+ delete _scene.surfacesToChangeSortingMode[key];
+ delete _scene.surfacesToChangeMaterial[key];
+ delete _scene.surfacesToChangeBSPLevel[key];
+ }
+ }
+ }
+ }
+
+ return s;
+ }
+
+ /**
+ * Набор вершин объекта. Ключами ассоциативного массива являются идентификаторы вершин, значениями - экземпляры вершин.
+ */
+ public function get vertices():Map {
+ return _vertices.clone();
+ }
+
+ /**
+ * Набор граней объекта. Ключами ассоциативного массива являются идентификаторы граней, значениями - экземпляры граней.
+ */
+ public function get faces():Map {
+ return _faces.clone();
+ }
+
+ /**
+ * Набор поверхностей объекта. Ключами ассоциативного массива являются идентификаторы поверхностей, значениями - экземпляры поверхностей.
+ */
+ public function get surfaces():Map {
+ return _surfaces.clone();
+ }
+
+ /**
+ * Получение вершины объекта по её идентификатору.
+ *
+ * @param id идентификатор вершины
+ *
+ * @return экземпляр вершины с указанным идентификатором
+ *
+ * @throws alternativa.engine3d.errors.VertexNotFoundError объект не содержит вершину с указанным идентификатором
+ * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
+ */
+ public function getVertexById(id:Object):Vertex {
+ if (id == null) {
+ throw new VertexNotFoundError(null, this);
+ }
+ if (_vertices[id] == undefined) {
+ // Если нет вершины с таким ID
+ throw new VertexNotFoundError(id, this);
+ } else {
+ if (_vertices[id] is Vertex) {
+ return _vertices[id];
+ } else {
+ // ID некорректный
+ throw new InvalidIDError(id, this);
+ }
+ }
+ }
+
+ /**
+ * Получение идентификатора вершины объекта.
+ *
+ * @param экземпляр вершины
+ *
+ * @return идентификатор указанной вершины
+ *
+ * @throws alternativa.engine3d.errors.VertexNotFoundError объект не содержит указанную вершину
+ */
+ public function getVertexId(vertex:Vertex):Object {
+ if (vertex == null) {
+ throw new VertexNotFoundError(null, this);
+ }
+ if (vertex._mesh != this) {
+ // Если вершина не в меше
+ throw new VertexNotFoundError(vertex, this);
+ }
+ for (var i:Object in _vertices) {
+ if (_vertices[i] == vertex) {
+ return i;
+ }
+ }
+ throw new VertexNotFoundError(vertex, this);
+ }
+
+ /**
+ * Проверка наличия вершины в объекте.
+ *
+ * @param vertex экземпляр класса alternativa.engine3d.core.Vertex или идентификатор вершины
+ *
+ * @return true, если объект содержит указанную вершину, иначе false
+ *
+ * @throws alternativa.engine3d.errors.VertexNotFoundError в качестве vertex был передан null
+ * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
+ *
+ * @see Vertex
+ */
+ public function hasVertex(vertex:Object):Boolean {
+ if (vertex == null) {
+ throw new VertexNotFoundError(null, this);
+ }
+ if (vertex is Vertex) {
+ // Проверка вершины
+ return vertex._mesh == this;
+ } else {
+ // Проверка ID вершины
+ if (_vertices[vertex] != undefined) {
+ // По этому ID есть объект
+ if (_vertices[vertex] is Vertex) {
+ // Объект является вершиной
+ return true;
+ } else {
+ // ID некорректный
+ throw new InvalidIDError(vertex, this);
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Получение грани объекта по ее идентификатору.
+ *
+ * @param id идентификатор грани
+ *
+ * @return экземпляр грани с указанным идентификатором
+ *
+ * @throws alternativa.engine3d.errors.FaceNotFoundError объект не содержит грань с указанным идентификатором
+ * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
+ */
+ public function getFaceById(id:Object):Face {
+ if (id == null) {
+ throw new FaceNotFoundError(null, this);
+ }
+ if (_faces[id] == undefined) {
+ // Если нет грани с таким ID
+ throw new FaceNotFoundError(id, this);
+ } else {
+ if (_faces[id] is Face) {
+ return _faces[id];
+ } else {
+ // ID некорректный
+ throw new InvalidIDError(id, this);
+ }
+ }
+ }
+
+ /**
+ * Получение идентификатора грани объекта.
+ *
+ * @param face экземпляр грани
+ *
+ * @return идентификатор указанной грани
+ *
+ * @throws alternativa.engine3d.errors.FaceNotFoundError объект не содержит указанную грань
+ */
+ public function getFaceId(face:Face):Object {
+ if (face == null) {
+ throw new FaceNotFoundError(null, this);
+ }
+ if (face._mesh != this) {
+ // Если грань не в меше
+ throw new FaceNotFoundError(face, this);
+ }
+ for (var i:Object in _faces) {
+ if (_faces[i] == face) {
+ return i;
+ }
+ }
+ throw new FaceNotFoundError(face, this);
+ }
+
+ /**
+ * Проверка наличия грани в объекте.
+ *
+ * @param face экземпляр класса Face или идентификатор грани
+ *
+ * @return true, если объект содержит указанную грань, иначе false
+ *
+ * @throws alternativa.engine3d.errors.FaceNotFoundError в качестве face был указан null
+ * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
+ */
+ public function hasFace(face:Object):Boolean {
+ if (face == null) {
+ throw new FaceNotFoundError(null, this);
+ }
+ if (face is Face) {
+ // Проверка грани
+ return face._mesh == this;
+ } else {
+ // Проверка ID грани
+ if (_faces[face] != undefined) {
+ // По этому ID есть объект
+ if (_faces[face] is Face) {
+ // Объект является гранью
+ return true;
+ } else {
+ // ID некорректный
+ throw new InvalidIDError(face, this);
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Получение поверхности объекта по ее идентификатору
+ *
+ * @param id идентификатор поверхности
+ *
+ * @return экземпляр поверхности с указанным идентификатором
+ *
+ * @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит поверхность с указанным идентификатором
+ * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
+ */
+ public function getSurfaceById(id:Object):Surface {
+ if (id == null) {
+ throw new SurfaceNotFoundError(null, this);
+ }
+ if (_surfaces[id] == undefined) {
+ // Если нет поверхности с таким ID
+ throw new SurfaceNotFoundError(id, this);
+ } else {
+ if (_surfaces[id] is Surface) {
+ return _surfaces[id];
+ } else {
+ // ID некорректный
+ throw new InvalidIDError(id, this);
+ }
+ }
+ }
+
+ /**
+ * Получение идентификатора поверхности объекта.
+ *
+ * @param surface экземпляр поверхности
+ *
+ * @return идентификатор указанной поверхности
+ *
+ * @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит указанную поверхность
+ */
+ public function getSurfaceId(surface:Surface):Object {
+ if (surface == null) {
+ throw new SurfaceNotFoundError(null, this);
+ }
+ if (surface._mesh != this) {
+ // Если поверхность не в меше
+ throw new SurfaceNotFoundError(surface, this);
+ }
+ for (var i:Object in _surfaces) {
+ if (_surfaces[i] == surface) {
+ return i;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Проверка наличия поверхности в объекте.
+ *
+ * @param surface экземпляр класса Surface или идентификатор поверхности
+ *
+ * @return true, если объект содержит указанную поверхность, иначе false
+ *
+ * @throws alternativa.engine3d.errors.SurfaceNotFoundError в качестве surface был передан null
+ * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
+ */
+ public function hasSurface(surface:Object):Boolean {
+ if (surface == null) {
+ throw new SurfaceNotFoundError(null, this);
+ }
+ if (surface is Surface) {
+ // Проверка поверхности
+ return surface._mesh == this;
+ } else {
+ // Проверка ID поверхности
+ if (_surfaces[surface] != undefined) {
+ // По этому ID есть объект
+ if (_surfaces[surface] is Surface) {
+ // Объект является поверхностью
+ return true;
+ } else {
+ // ID некорректный
+ throw new InvalidIDError(surface, this);
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ override protected function defaultName():String {
+ return "mesh" + ++counter;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ override public function toString():String {
+ return "[" + ObjectUtils.getClassName(this) + " " + _name + " vertices: " + _vertices.length + " faces: " + _faces.length + "]";
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected override function createEmptyObject():Object3D {
+ return new Mesh();
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/core/Object3D.as b/Alternativa3D6/6.0/alternativa/engine3d/core/Object3D.as
new file mode 100644
index 0000000..806b15d
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/core/Object3D.as
@@ -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;
+
+ /**
+ * Базовый класс для объектов, находящихся в сцене. Класс реализует иерархию объектов сцены, а также содержит сведения
+ * о трансформации объекта как единого целого.
+ *
+ * Масштабирование, ориентация и положение объекта задаются в родительской системе координат. Результирующая
+ * локальная трансформация является композицией операций масштабирования, поворотов объекта относительно осей
+ * X, Y, Z и параллельного переноса центра объекта из начала координат.
+ * Операции применяются в порядке их перечисления.
+ *
+ *
Глобальная трансформация (в системе координат корневого объекта сцены) является композицией трансформаций
+ * самого объекта и всех его предков по иерархии объектов сцены.
+ */
+ 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;
+ }
+
+ /**
+ * Клонирование объекта. Для реализации собственного клонирования наследники должны переопределять методы
+ * createEmptyObject() и clonePropertiesFrom().
+ *
+ * @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 любой дочерний объект с заданным именем или null в случае отсутствия таких объектов
+ */
+ public function getChildByName(name:String):Object3D {
+ for (var key:* in _children) {
+ var child:Object3D = key;
+ if (child._name == name) {
+ return child;
+ }
+ }
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/core/Scene3D.as b/Alternativa3D6/6.0/alternativa/engine3d/core/Scene3D.as
new file mode 100644
index 0000000..3ce3e17
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/core/Scene3D.as
@@ -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;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/core/Space.as b/Alternativa3D6/6.0/alternativa/engine3d/core/Space.as
new file mode 100644
index 0000000..41a783b
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/core/Space.as
@@ -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;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/core/Sprite3D.as b/Alternativa3D6/6.0/alternativa/engine3d/core/Sprite3D.as
new file mode 100644
index 0000000..88cb969
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/core/Sprite3D.as
@@ -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;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/core/Surface.as b/Alternativa3D6/6.0/alternativa/engine3d/core/Surface.as
new file mode 100644
index 0000000..bb41ecc
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/core/Surface.as
@@ -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;
+
+ /**
+ * Поверхность — набор граней, объединённых в группу. Поверхности используются для установки материалов,
+ * визуализирующих грани объекта.
+ */
+ 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 экземпляр класса alternativa.engine3d.core.Face или идентификатор грани полигонального объекта
+ *
+ * @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 экземпляр класса alternativa.engine3d.core.Face или идентификатор грани полигонального объекта
+ *
+ * @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;
+ }
+ }
+ }
+
+ /**
+ * Идентификатор поверхности в полигональном объекте. Если поверхность не принадлежит ни одному объекту,
+ * значение идентификатора равно null.
+ */
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/core/Vertex.as b/Alternativa3D6/6.0/alternativa/engine3d/core/Vertex.as
new file mode 100644
index 0000000..1360415
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/core/Vertex.as
@@ -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;
+ }
+
+ /**
+ * Множество граней, которым принадлежит вершина. Каждый элемент множества является объектом класса
+ * altertnativa.engine3d.core.Face.
+ *
+ * @see Face
+ */
+ public function get faces():Set {
+ return _faces.clone();
+ }
+
+ /**
+ * Идентификатор вершины в полигональном объекте. Если вершина не принадлежит полигональному объекту, возвращается null.
+ */
+ 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) + "]";
+ }
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/display/Canvas.as b/Alternativa3D6/6.0/alternativa/engine3d/display/Canvas.as
new file mode 100644
index 0000000..2d2824d
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/display/Canvas.as
@@ -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);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/display/DisplayItem.as b/Alternativa3D6/6.0/alternativa/engine3d/display/DisplayItem.as
new file mode 100644
index 0000000..b19f0b2
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/display/DisplayItem.as
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/display/Skin.as b/Alternativa3D6/6.0/alternativa/engine3d/display/Skin.as
new file mode 100644
index 0000000..79ce91d
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/display/Skin.as
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/display/View.as b/Alternativa3D6/6.0/alternativa/engine3d/display/View.as
new file mode 100644
index 0000000..2e5b4f2
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/display/View.as
@@ -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) {
+ // Отправляем сигнал об изменении плоскостей отсечения
+ // ...
+ }
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/errors/Alternativa3DError.as b/Alternativa3D6/6.0/alternativa/engine3d/errors/Alternativa3DError.as
new file mode 100644
index 0000000..5fdbbe4
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/errors/Alternativa3DError.as
@@ -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";
+ }
+ }
+}
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/errors/FaceExistsError.as b/Alternativa3D6/6.0/alternativa/engine3d/errors/FaceExistsError.as
new file mode 100644
index 0000000..ecc7f59
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/errors/FaceExistsError.as
@@ -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";
+ }
+ }
+}
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/errors/FaceNeedMoreVerticesError.as b/Alternativa3D6/6.0/alternativa/engine3d/errors/FaceNeedMoreVerticesError.as
new file mode 100644
index 0000000..1417105
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/errors/FaceNeedMoreVerticesError.as
@@ -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";
+ }
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/errors/FaceNotFoundError.as b/Alternativa3D6/6.0/alternativa/engine3d/errors/FaceNotFoundError.as
new file mode 100644
index 0000000..912d6a4
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/errors/FaceNotFoundError.as
@@ -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";
+ }
+ }
+}
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/errors/InvalidIDError.as b/Alternativa3D6/6.0/alternativa/engine3d/errors/InvalidIDError.as
new file mode 100644
index 0000000..fbf4666
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/errors/InvalidIDError.as
@@ -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";
+ }
+ }
+}
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/errors/InvalidSortingModeError.as b/Alternativa3D6/6.0/alternativa/engine3d/errors/InvalidSortingModeError.as
new file mode 100644
index 0000000..928788f
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/errors/InvalidSortingModeError.as
@@ -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";
+ }
+ }
+}
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/errors/Object3DHierarchyError.as b/Alternativa3D6/6.0/alternativa/engine3d/errors/Object3DHierarchyError.as
new file mode 100644
index 0000000..585d159
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/errors/Object3DHierarchyError.as
@@ -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";
+ }
+ }
+}
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/errors/Object3DNotFoundError.as b/Alternativa3D6/6.0/alternativa/engine3d/errors/Object3DNotFoundError.as
new file mode 100644
index 0000000..d1ddd1e
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/errors/Object3DNotFoundError.as
@@ -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";
+ }
+ }
+}
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/errors/ObjectExistsError.as b/Alternativa3D6/6.0/alternativa/engine3d/errors/ObjectExistsError.as
new file mode 100644
index 0000000..fdd40ba
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/errors/ObjectExistsError.as
@@ -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";
+ }
+ }
+}
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/errors/ObjectNotFoundError.as b/Alternativa3D6/6.0/alternativa/engine3d/errors/ObjectNotFoundError.as
new file mode 100644
index 0000000..60ace36
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/errors/ObjectNotFoundError.as
@@ -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";
+ }
+ }
+}
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/errors/SurfaceExistsError.as b/Alternativa3D6/6.0/alternativa/engine3d/errors/SurfaceExistsError.as
new file mode 100644
index 0000000..e13bee7
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/errors/SurfaceExistsError.as
@@ -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";
+ }
+ }
+}
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/errors/SurfaceNotFoundError.as b/Alternativa3D6/6.0/alternativa/engine3d/errors/SurfaceNotFoundError.as
new file mode 100644
index 0000000..05457bf
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/errors/SurfaceNotFoundError.as
@@ -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";
+ }
+ }
+}
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/errors/VertexExistsError.as b/Alternativa3D6/6.0/alternativa/engine3d/errors/VertexExistsError.as
new file mode 100644
index 0000000..9f0fff5
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/errors/VertexExistsError.as
@@ -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";
+ }
+ }
+}
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/errors/VertexNotFoundError.as b/Alternativa3D6/6.0/alternativa/engine3d/errors/VertexNotFoundError.as
new file mode 100644
index 0000000..edf80aa
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/errors/VertexNotFoundError.as
@@ -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";
+ }
+ }
+}
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/materials/FillMaterial.as b/Alternativa3D6/6.0/alternativa/engine3d/materials/FillMaterial.as
new file mode 100644
index 0000000..5ea79d1
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/materials/FillMaterial.as
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/materials/Material.as b/Alternativa3D6/6.0/alternativa/engine3d/materials/Material.as
new file mode 100644
index 0000000..9a65a5d
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/materials/Material.as
@@ -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 {}
+
+ /**
+ * Клонирование объекта. При расширении класса, в наследниках должны быть переопределены вспомогательные
+ * методы create() и cloneProperties().
+ * @return клон объекта
+ * @see #create()
+ * @see #cloneProperties()
+ */
+ public function clone():Material {
+ var res:Material = create();
+ res.cloneProperties(this);
+ return res;
+ }
+
+ /**
+ * Вспомогательный для клонирования метод. Создание нового экземпляра, который выступит в качестве клона после копирования в него свойств.
+ * Вызывается внутри метода clone().
+ * При переопределении метод должен перекрываться полностью.
+ * @return новый материал
+ * @see #clone()
+ */
+ protected function create():Material {
+ return new Material();
+ }
+
+ /**
+ * Вспомогательный для клонирования метод. Копирование свойств из другого объекта. Вызывается внутри метода clone()
+ * объектом, который создан с помощью метода create().
+ * При переопределении нужно вызывать super.cloneProperties().
+ * @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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/materials/SpaceMaterial.as b/Alternativa3D6/6.0/alternativa/engine3d/materials/SpaceMaterial.as
new file mode 100644
index 0000000..c5e2f94
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/materials/SpaceMaterial.as
@@ -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;
+ }
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/materials/SpriteMaterial.as b/Alternativa3D6/6.0/alternativa/engine3d/materials/SpriteMaterial.as
new file mode 100644
index 0000000..3f0cd34
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/materials/SpriteMaterial.as
@@ -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;
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/materials/SurfaceMaterial.as b/Alternativa3D6/6.0/alternativa/engine3d/materials/SurfaceMaterial.as
new file mode 100644
index 0000000..ec7d513
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/materials/SurfaceMaterial.as
@@ -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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/sorting/BSPNode.as b/Alternativa3D6/6.0/alternativa/engine3d/sorting/BSPNode.as
new file mode 100644
index 0000000..fd47a5d
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/sorting/BSPNode.as
@@ -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 {
+
+ }
+*/
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/sorting/DistanceNode.as b/Alternativa3D6/6.0/alternativa/engine3d/sorting/DistanceNode.as
new file mode 100644
index 0000000..c1182af
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/sorting/DistanceNode.as
@@ -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);
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/sorting/DistancePrimitive.as b/Alternativa3D6/6.0/alternativa/engine3d/sorting/DistancePrimitive.as
new file mode 100644
index 0000000..7426fdb
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/sorting/DistancePrimitive.as
@@ -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();
+
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/sorting/FaceBSPPrimitive.as b/Alternativa3D6/6.0/alternativa/engine3d/sorting/FaceBSPPrimitive.as
new file mode 100644
index 0000000..de91c50
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/sorting/FaceBSPPrimitive.as
@@ -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);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/sorting/FaceDistancePrimitive.as b/Alternativa3D6/6.0/alternativa/engine3d/sorting/FaceDistancePrimitive.as
new file mode 100644
index 0000000..0afd2cb
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/sorting/FaceDistancePrimitive.as
@@ -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);
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/sorting/FaceNonePrimitive.as b/Alternativa3D6/6.0/alternativa/engine3d/sorting/FaceNonePrimitive.as
new file mode 100644
index 0000000..140271d
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/sorting/FaceNonePrimitive.as
@@ -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);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/sorting/Node.as b/Alternativa3D6/6.0/alternativa/engine3d/sorting/Node.as
new file mode 100644
index 0000000..1779686
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/sorting/Node.as
@@ -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;
+
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/sorting/NonePrimitive.as b/Alternativa3D6/6.0/alternativa/engine3d/sorting/NonePrimitive.as
new file mode 100644
index 0000000..6bdda98
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/sorting/NonePrimitive.as
@@ -0,0 +1,13 @@
+package alternativa.engine3d.sorting {
+
+ import alternativa.engine3d.*;
+
+ use namespace alternativa3d;
+
+ public class NonePrimitive extends Primitive {
+
+ // Уровень сортировки
+ alternativa3d var sortingLevel:SortingLevel;
+
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/sorting/Primitive.as b/Alternativa3D6/6.0/alternativa/engine3d/sorting/Primitive.as
new file mode 100644
index 0000000..f52174d
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/sorting/Primitive.as
@@ -0,0 +1,8 @@
+package alternativa.engine3d.sorting {
+
+ import alternativa.engine3d.*;
+
+ use namespace alternativa3d;
+
+ public class Primitive {}
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/sorting/SortingLevel.as b/Alternativa3D6/6.0/alternativa/engine3d/sorting/SortingLevel.as
new file mode 100644
index 0000000..892ca51
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/sorting/SortingLevel.as
@@ -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];
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/sorting/SortingMode.as b/Alternativa3D6/6.0/alternativa/engine3d/sorting/SortingMode.as
new file mode 100644
index 0000000..15e4814
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/sorting/SortingMode.as
@@ -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;
+
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/sorting/SpaceDistancePrimitive.as b/Alternativa3D6/6.0/alternativa/engine3d/sorting/SpaceDistancePrimitive.as
new file mode 100644
index 0000000..79549b5
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/sorting/SpaceDistancePrimitive.as
@@ -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);
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/sorting/SpaceNonePrimitive.as b/Alternativa3D6/6.0/alternativa/engine3d/sorting/SpaceNonePrimitive.as
new file mode 100644
index 0000000..d3633b2
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/sorting/SpaceNonePrimitive.as
@@ -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);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/sorting/SpriteDistancePrimitive.as b/Alternativa3D6/6.0/alternativa/engine3d/sorting/SpriteDistancePrimitive.as
new file mode 100644
index 0000000..8fd894b
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/sorting/SpriteDistancePrimitive.as
@@ -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);
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Alternativa3D6/6.0/alternativa/engine3d/sorting/SpriteNonePrimitive.as b/Alternativa3D6/6.0/alternativa/engine3d/sorting/SpriteNonePrimitive.as
new file mode 100644
index 0000000..4b3e185
--- /dev/null
+++ b/Alternativa3D6/6.0/alternativa/engine3d/sorting/SpriteNonePrimitive.as
@@ -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);
+ }
+ }
+ }
+}
\ No newline at end of file