From ecf60d961b89274acc11e6994696ab550b750994 Mon Sep 17 00:00:00 2001 From: Pyogenics Date: Sat, 28 Sep 2024 17:40:49 +0100 Subject: [PATCH] Add A3D6 --- .../6.0/alternativa/Alternativa3D.as | 14 + .../6.0/alternativa/engine3d/alternativa3d.as | 3 + .../6.0/alternativa/engine3d/core/Camera3D.as | 564 +++++++++ .../6.0/alternativa/engine3d/core/Face.as | 483 ++++++++ .../6.0/alternativa/engine3d/core/Mesh.as | 1084 +++++++++++++++++ .../6.0/alternativa/engine3d/core/Object3D.as | 645 ++++++++++ .../6.0/alternativa/engine3d/core/Scene3D.as | 405 ++++++ .../6.0/alternativa/engine3d/core/Space.as | 700 +++++++++++ .../6.0/alternativa/engine3d/core/Sprite3D.as | 246 ++++ .../6.0/alternativa/engine3d/core/Surface.as | 788 ++++++++++++ .../6.0/alternativa/engine3d/core/Vertex.as | 174 +++ .../alternativa/engine3d/display/Canvas.as | 73 ++ .../engine3d/display/DisplayItem.as | 18 + .../6.0/alternativa/engine3d/display/Skin.as | 68 ++ .../6.0/alternativa/engine3d/display/View.as | 117 ++ .../engine3d/errors/Alternativa3DError.as | 27 + .../engine3d/errors/FaceExistsError.as | 35 + .../errors/FaceNeedMoreVerticesError.as | 29 + .../engine3d/errors/FaceNotFoundError.as | 34 + .../engine3d/errors/InvalidIDError.as | 34 + .../errors/InvalidSortingModeError.as | 43 + .../engine3d/errors/Object3DHierarchyError.as | 28 + .../engine3d/errors/Object3DNotFoundError.as | 22 + .../engine3d/errors/ObjectExistsError.as | 26 + .../engine3d/errors/ObjectNotFoundError.as | 26 + .../engine3d/errors/SurfaceExistsError.as | 22 + .../engine3d/errors/SurfaceNotFoundError.as | 31 + .../engine3d/errors/VertexExistsError.as | 22 + .../engine3d/errors/VertexNotFoundError.as | 28 + .../engine3d/materials/FillMaterial.as | 197 +++ .../engine3d/materials/Material.as | 169 +++ .../engine3d/materials/SpaceMaterial.as | 66 + .../engine3d/materials/SpriteMaterial.as | 27 + .../engine3d/materials/SurfaceMaterial.as | 46 + .../alternativa/engine3d/sorting/BSPNode.as | 49 + .../engine3d/sorting/DistanceNode.as | 74 ++ .../engine3d/sorting/DistancePrimitive.as | 17 + .../engine3d/sorting/FaceBSPPrimitive.as | 47 + .../engine3d/sorting/FaceDistancePrimitive.as | 42 + .../engine3d/sorting/FaceNonePrimitive.as | 41 + .../6.0/alternativa/engine3d/sorting/Node.as | 17 + .../engine3d/sorting/NonePrimitive.as | 13 + .../alternativa/engine3d/sorting/Primitive.as | 8 + .../engine3d/sorting/SortingLevel.as | 86 ++ .../engine3d/sorting/SortingMode.as | 10 + .../sorting/SpaceDistancePrimitive.as | 42 + .../engine3d/sorting/SpaceNonePrimitive.as | 41 + .../sorting/SpriteDistancePrimitive.as | 42 + .../engine3d/sorting/SpriteNonePrimitive.as | 41 + 49 files changed, 6864 insertions(+) create mode 100644 Alternativa3D6/6.0/alternativa/Alternativa3D.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/alternativa3d.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/core/Camera3D.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/core/Face.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/core/Mesh.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/core/Object3D.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/core/Scene3D.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/core/Space.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/core/Sprite3D.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/core/Surface.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/core/Vertex.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/display/Canvas.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/display/DisplayItem.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/display/Skin.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/display/View.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/errors/Alternativa3DError.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/errors/FaceExistsError.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/errors/FaceNeedMoreVerticesError.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/errors/FaceNotFoundError.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/errors/InvalidIDError.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/errors/InvalidSortingModeError.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/errors/Object3DHierarchyError.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/errors/Object3DNotFoundError.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/errors/ObjectExistsError.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/errors/ObjectNotFoundError.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/errors/SurfaceExistsError.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/errors/SurfaceNotFoundError.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/errors/VertexExistsError.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/errors/VertexNotFoundError.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/materials/FillMaterial.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/materials/Material.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/materials/SpaceMaterial.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/materials/SpriteMaterial.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/materials/SurfaceMaterial.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/sorting/BSPNode.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/sorting/DistanceNode.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/sorting/DistancePrimitive.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/sorting/FaceBSPPrimitive.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/sorting/FaceDistancePrimitive.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/sorting/FaceNonePrimitive.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/sorting/Node.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/sorting/NonePrimitive.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/sorting/Primitive.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/sorting/SortingLevel.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/sorting/SortingMode.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/sorting/SpaceDistancePrimitive.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/sorting/SpaceNonePrimitive.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/sorting/SpriteDistancePrimitive.as create mode 100644 Alternativa3D6/6.0/alternativa/engine3d/sorting/SpriteNonePrimitive.as diff --git a/Alternativa3D6/6.0/alternativa/Alternativa3D.as b/Alternativa3D6/6.0/alternativa/Alternativa3D.as new file mode 100644 index 0000000..d1950b1 --- /dev/null +++ b/Alternativa3D6/6.0/alternativa/Alternativa3D.as @@ -0,0 +1,14 @@ +package alternativa { + + /** + * Класс содержит информацию о версии библиотеки. + * Также используется для интеграции библиотеки в среду разработки Adobe Flash. + */ + public class Alternativa3D { + + /** + * Версия библиотеки в формате: версия.подверсия.сборка + */ + public static const version:String = "6.0.0"; + } +} \ No newline at end of file diff --git a/Alternativa3D6/6.0/alternativa/engine3d/alternativa3d.as b/Alternativa3D6/6.0/alternativa/engine3d/alternativa3d.as new file mode 100644 index 0000000..8bce40e --- /dev/null +++ b/Alternativa3D6/6.0/alternativa/engine3d/alternativa3d.as @@ -0,0 +1,3 @@ +package alternativa.engine3d { + public namespace alternativa3d = "http://alternativaplatform.com/en/alternativa3d"; +} diff --git a/Alternativa3D6/6.0/alternativa/engine3d/core/Camera3D.as b/Alternativa3D6/6.0/alternativa/engine3d/core/Camera3D.as new file mode 100644 index 0000000..3674bed --- /dev/null +++ b/Alternativa3D6/6.0/alternativa/engine3d/core/Camera3D.as @@ -0,0 +1,564 @@ +package alternativa.engine3d.core { + import alternativa.engine3d.*; + import alternativa.engine3d.display.Canvas; + import alternativa.engine3d.display.DisplayItem; + import alternativa.engine3d.display.Skin; + import alternativa.engine3d.display.View; + import alternativa.engine3d.sorting.Node; + import alternativa.types.Matrix3D; + import alternativa.types.Set; + import alternativa.utils.MathUtils; + + import flash.display.DisplayObjectContainer; + import flash.utils.Dictionary; + + use namespace alternativa3d; + + public class Camera3D extends Object3D { + + // Инкремент количества объектов + private static var counter:uint = 0; + + /** + * @private + * Поле зрения + */ + alternativa3d var _fov:Number = MathUtils.DEG90; + /** + * @private + * Фокусное расстояние + */ + alternativa3d var focalLength:Number; + /** + * @private + * Перспективное искажение + */ + alternativa3d var focalDistortion:Number; + + /** + * @private + * Вид из камеры + */ + alternativa3d var _view:View; + + //Половина ширины вьюпорта + private var halfWidth:Number; + + //Половина высоты вьюпорта + private var halfHeight:Number; + + /** + * @private + * Режим отрисовки + */ + alternativa3d var _orthographic:Boolean = false; + + /** + * @private + * Матрица перевода из системы координат камеры в глобальную + */ + alternativa3d var globalMatrix:Matrix3D = new Matrix3D(); + /** + * @private + * Матрица перевода из глобальной системы координат в систему камеры + */ + alternativa3d var inverseGlobalMatrix:Matrix3D = new Matrix3D(); + + // Масштаб + alternativa3d var _zoom:Number = 1; + + // Реестр отрисованных канвасов + private var canvases:Dictionary = new Dictionary(); + + // Список канвасов на удаление + private var canvasesToRemove:Set = new Set(); + + /** + * Создание экземпляра камеры. + * + * @param name имя экземпляра + */ + public function Camera3D(name:String = null) { + super(name); + } + + override protected function transform():void { + super.transform(); + // Если есть вьюпорт + if (_view != null) { + // Помечаем камеру на глобальную трансформацию + _scene.camerasToRender[this] = true; + } + } + + override protected function move():void { + super.move(); + // Если есть вьюпорт + if (_view != null) { + // Помечаем камеру на глобальную трансформацию + _scene.camerasToRender[this] = true; + } + } + + alternativa3d function render():void { + trace(this, "render"); + + // Если у пространства сцены есть материал + if (_scene._space._material != null) { + + // Расчитываем половины ширины и высоты вьюпорта + halfWidth = _view._width*0.5; + halfHeight = _view._height*0.5; + + // Если перспектива + if (!_orthographic) { + // Вычисляем фокусное расстояние + focalLength = Math.sqrt(halfWidth*halfWidth + halfHeight*halfHeight)/Math.tan(0.5*_fov); + // Вычисляем минимальное (однопиксельное) искажение перспективной коррекции + focalDistortion = 1/(focalLength*focalLength); + } + + // Расчитываем глобальную трансформацию + globalMatrix.copy(spaceMatrix); + globalMatrix.combine(space.globalMatrix); + + // Расчитываем инверсную глобальную трансформацию + inverseGlobalMatrix.copy(globalMatrix); + inverseGlobalMatrix.invert(); + + // Создание канваса пространства сцены, если его ещё нет + if (_view.canvas == null) { + _view.canvas = Canvas.create(); + _view.addChild(_view.canvas); + _view.canvas.x = halfWidth; + _view.canvas.y = halfHeight; + canvases[_scene._space] = _view.canvas; + } + + // Отрисовка пространства сцены + renderSpace(_scene._space, _view.canvas); + + } else { + // Зачищаем канвас вьюпорта + removeViewCanvas(); + } + + // Снимаем пометки об отрисовке + delete _scene.camerasToRender[this]; + } + + private function renderSpace(space:Space, canvas:Canvas):void { + trace("render space", space); + + // Если пространство сцены + if (space.space == null) { + // Берём в качестве матриц пространства глобальные + space.inverseCameraMatrix.copy(globalMatrix); + space.cameraMatrix.copy(inverseGlobalMatrix); + } else { + // Считаем матрицу перевода из системы координат пространства в систему камеры + space.cameraMatrix.copy(space.globalMatrix) + space.cameraMatrix.combine(inverseGlobalMatrix); + // Считаем матрицу перевода из системы координат камеры в систему пространства + space.inverseCameraMatrix.copy(space.cameraMatrix); + space.inverseCameraMatrix.invert(); + } + + // Направление камеры в пространстве + space.direction.x = space.inverseCameraMatrix.c; + space.direction.y = space.inverseCameraMatrix.g; + space.direction.z = space.inverseCameraMatrix.k; + + // Расчёт направления и плоскостей отсечения в пространстве + if (_orthographic) { + // Масштабируем матрицу камеры + space.cameraMatrix.scale(_zoom, _zoom, _zoom); + // Расчёт плоскостей отсечения + space.calculateOrthographicPlanes(halfWidth, halfHeight, _zoom) + } else { + // Нормализуем направление камеры + space.direction.normalize(); + // Расчёт плоскостей отсечения + space.calculatePerspectivePlanes(halfWidth, halfHeight, focalLength) + } + + // Отрисовка BSP-дерева + /*if (space.root != null) { + canvas.previousItem = null; + canvas.currentItem = canvas.firstItem; + renderNode(space.root, canvas); + }*/ + + // Удаление помеченных канвасов + for (var key:* in canvasesToRemove) { + var c:Canvas = key; + var parent:DisplayObjectContainer = c.parent; + // Если есть объект перед канвасом + if (c.previous != null) { + // Устанавливаем ему ссылку на следующий + c.previous.next = c.next; + } else { + // Устанавливаем первый объект в родительском канвасе + (parent as Canvas).firstItem = c.next; + } + // Если после канваса есть канвас + if (c.next is Canvas) { + (c.next as Canvas).previous = c.previous; + } + // Удаляем из списка отображения + parent.removeChild(c); + // Отправляем на реиспользование + Canvas.destroy(c); + } + + // Удаление лишних отрисовочных объектов + if (canvas.previous != null) { + // Обрываем список + canvas.previous.next = null; + } + var item:DisplayItem = canvas.currentItem; + while (item != null) { + // Сохраняем следующий + var next:DisplayItem = item.next; + // Удаляем из канваса + canvas.removeChild(item); + // Удаляем + (item is Skin) ? Skin.destroy(item as Skin) : Canvas.destroy(item as Canvas); + // Следующий устанавливаем текущим + item = next; + } + } + + private function renderNode(node:Node, container:Canvas):void { +/* + var primitives:Set; + if (node is DistanceNode) { + primitives = (node as DistanceNode).primitives; + for (var key:* in primitives) { + // Если точечный примитив грани + if (key is FaceDistancePrimitive) { + var face:Face = (key as FaceDistancePrimitive).face; + var skin:Skin; + // Пропускаем канвасы и помечаем их на удаление + while (container.currentItem is Canvas) { + // Помечаем на удаление + canvasesToRemove[container.currentItem] = true; + // Переключаемся на следующий объект + container.previousItem = container.currentItem; + container.currentItem = container.currentItem.next; + } + + // Если есть текущий объект + if (container.currentItem != null) { + // Берём текущий скин + skin = container.currentItem as Skin; + // Переключаемся на следующий объект + container.previousItem = skin; + container.currentItem = skin.next; + } else { + // Создаём новый скин + skin = Skin.create(); + // Вставляем скин в конец + container.addChild(skin); + + // Обновление списка в текущем канвасе + if (container.previousItem != null) { + container.previousItem.next = skin; + } else { + container.firstItem = skin; + } + + // Переключаемся на следующий объект + container.previousItem = skin; + } + + // Отрисовка скина + // ... + skin.graphics.beginFill(0xFFFF00, 0.3); + skin.graphics.drawCircle(0, 0, Math.random()*100); + + } else { + // Если примитив объекта + if (key is SpriteDistancePrimitive) { + + } else { + // Если примитив пространства + var space:Space = (key as SpaceDistancePrimitive).space; + var canvas:Canvas; + + // Если канвас есть в реестре + if ((canvas = canvases[space]) != null) { + // Если канвасы не совпадают + if (canvas != container.currentItem) { + // Разорвать связи с соседними объектами + // Если есть объект перед канвасом + if (canvas.previous != null) { + // Устанавливаем ему ссылку на следующий + canvas.previous.next = canvas.next; + } else { + // Устанавливаем первый объект в родительском канвасе + (canvas.parent as Canvas).firstItem = canvas.next; + } + // Если после канваса есть канвас + if (canvas.next is Canvas) { + (canvas.next as Canvas).previous = canvas.previous; + } + + // Создать новые связи + // Установка связи со следующим объектом + canvas.next = container.currentItem; + // Установка связи с предыдущим объектом + canvas.previous = container.previousItem; + + // Обновление списка в текущем канвасе + if (container.previousItem != null) { + container.previousItem.next = canvas; + } else { + container.firstItem = canvas; + } + + // Если есть текущий объект + if (container.currentItem != null) { + // Вставляем канвас перед текущим объектом + container.addChildAt(canvas, container.getChildIndex(container.currentItem)); + if (container.currentItem is Canvas) { + (container.currentItem as Canvas).previous = canvas; + } + } else { + // Вставляем канвас в конец + container.addChild(canvas); + } + + // Переключаемся на следующий объект + container.previousItem = canvas; + + // Удалить канвас из списка на удаление + delete canvasesToRemove[canvas]; + } else { + // Переключаемся на следующий объект + container.previousItem = canvas; + container.currentItem = canvas.next; + } + } else { + // Создаём новый канвас + canvas = new Canvas(); + // Сохраняем его в реестр + canvases[space] = canvas; + + // Создать связи + // Установка связи со следующим объектом + canvas.next = container.currentItem; + // Установка связи с предыдущим объектом + canvas.previous = container.previousItem; + + // Обновление списка в текущем канвасе + if (container.previousItem != null) { + container.previousItem.next = canvas; + } else { + container.firstItem = canvas; + } + + // Если есть текущий объект + if (container.currentItem != null) { + // Вставляем канвас перед текущим объектом + container.addChildAt(canvas, container.getChildIndex(container.currentItem)); + if (container.currentItem is Canvas) { + (container.currentItem as Canvas).previous = canvas; + } + } else { + // Вставляем канвас в конец + container.addChild(canvas); + } + + // Переключаемся на следующий объект + container.previousItem = canvas; + } + + // Отрисовка пространства в канвас + renderSpace(space, canvas); + } + } + } + } else { + + } +*/ + } + + alternativa3d function updateSpace(space:Space):void { + trace(this, "updateSpace", space); + + + + } + + alternativa3d function updateSpaceMaterial(space:Space):void { + trace(this, "updateSpaceMaterial", space); + + // Если канвас нарисован, перерисовать его + var canvas:Canvas; + if ((canvas = canvases[space]) != null) { + space._material.draw(canvas); + } + } + + + override protected function addToScene():void { + super.addToScene(); + + // Добавляем камеру в реестр сцены + if (_view != null) { + _scene.cameras[this] = true; + } + } + + override protected function removeFromScene():void { + super.removeFromScene(); + + // Если у камеры есть вьюпорт + if (_view != null) { + + // Зачищаем канвас вьюпорта + removeViewCanvas(); + + // Удаляем камеру из реестра сцены + delete _scene.cameras[this]; + + // Удаляем все пометки в сцене + delete _scene.camerasToRender[this]; + } + } + + private function removeViewCanvas():void { + if (_view.canvas != null) { + _view.removeChild(_view.canvas); + Canvas.destroy(_view.canvas); + _view.canvas = null; + delete canvases[_scene._space]; + } + } + + /** + * Поле вывода, в котором происходит отрисовка камеры. + */ + public function get view():View { + return _view; + } + + /** + * @private + */ + public function set view(value:View):void { + if (value != _view) { + // Если был вьюпорт + if (_view != null) { + // Зачищаем канвас вьюпорта + removeViewCanvas(); + // Удалить в нём ссылку на камеру + _view._camera = null; + } + // Если назначается вьюпорт + if (value != null) { + // Если у вьюпорта была камера + if (value._camera != null) { + // Отцепить от у неё вьюпорт + value._camera.view = null; + } + // Сохранить во вьюпорте ссылку на камеру + value._camera = this; + // Если есть сцена + if (_scene != null) { + // Добавляем камеру в реестр сцены + _scene.cameras[this] = true; + // Помечаем камеру на отрисовку + _scene.camerasToRender[this] = true; + } + } else { + if (_scene != null) { + // Удаляем камеру из реестра сцены + delete _scene.cameras[this]; + } + } + // Сохраняем вьюпорт + _view = value; + } + } + + /** + * Включение режима аксонометрической проекции. + * + * @default false + */ + public function get orthographic():Boolean { + return _orthographic; + } + + /** + * @private + */ + public function set orthographic(value:Boolean):void { + if (_orthographic != value) { + // Отправляем сигнал об изменении типа камеры + // ... + // Сохраняем новое значение + _orthographic = value; + } + } + + /** + * Угол поля зрения в радианах в режиме перспективной проекции. При изменении FOV изменяется фокусное расстояние + * камеры по формуле 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