mirror of
https://github.com/MapMakersAndProgrammers/alternativa3d-archive.git
synced 2025-10-26 01:49:05 -07:00
Add A3D7
This commit is contained in:
14
Alternativa3D7/7.0/alternativa/Alternativa3D.as
Normal file
14
Alternativa3D7/7.0/alternativa/Alternativa3D.as
Normal file
@@ -0,0 +1,14 @@
|
||||
package alternativa {
|
||||
|
||||
/**
|
||||
* Класс содержит информацию о версии библиотеки.
|
||||
* Также используется для интеграции библиотеки в среду разработки Adobe Flash.
|
||||
*/
|
||||
public class Alternativa3D {
|
||||
|
||||
/**
|
||||
* Версия библиотеки в формате: поколение.feature-версия.fix-версия
|
||||
*/
|
||||
public static const version:String = "7.0.0";
|
||||
}
|
||||
}
|
||||
3
Alternativa3D7/7.0/alternativa/engine3d/alternativa3d.as
Normal file
3
Alternativa3D7/7.0/alternativa/engine3d/alternativa3d.as
Normal file
@@ -0,0 +1,3 @@
|
||||
package alternativa.engine3d {
|
||||
public namespace alternativa3d = "http://alternativaplatform.com/en/alternativa3d";
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package alternativa.engine3d.containers {
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.core.Object3DContainer;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Контейнер, дочерние объекты которого отрисовываются по удалённости от камеры
|
||||
*/
|
||||
public class AverageZContainer extends Object3DContainer {
|
||||
|
||||
static private const averageZ:Vector.<Number> = new Vector.<Number>();
|
||||
static private const center:Vector.<Number> = Vector.<Number>([0, 0, 0]);
|
||||
static private const cameraCenter:Vector.<Number> = new Vector.<Number>(3, true);
|
||||
static private const sortingStack:Vector.<int> = new Vector.<int>();
|
||||
|
||||
override protected function drawVisibleChildren(camera:Camera3D, object:Object3D, canvas:Canvas):void {
|
||||
var i:int;
|
||||
var j:int;
|
||||
var l:int = 0;
|
||||
var r:int = numVisibleChildren - 1;
|
||||
var child:Object3D;
|
||||
var sortingStackIndex:int;
|
||||
var sortingLeft:Number;
|
||||
var sortingMedian:Number;
|
||||
var sortingRight:Number;
|
||||
var sortingChild:Object3D;
|
||||
// Сортировка
|
||||
for (i = 0; i < numVisibleChildren; i++) {
|
||||
child = visibleChildren[i];
|
||||
child.cameraMatrix.transformVectors(center, cameraCenter);
|
||||
averageZ[i] = cameraCenter[0]*cameraCenter[0] + cameraCenter[1]*cameraCenter[1] + cameraCenter[2]*cameraCenter[2];
|
||||
}
|
||||
sortingStack[0] = l;
|
||||
sortingStack[1] = r;
|
||||
sortingStackIndex = 2;
|
||||
while (sortingStackIndex > 0) {
|
||||
j = r = sortingStack[--sortingStackIndex];
|
||||
i = l = sortingStack[--sortingStackIndex];
|
||||
sortingMedian = averageZ[(r + l) >> 1];
|
||||
do {
|
||||
while ((sortingLeft = averageZ[i]) > sortingMedian) i++;
|
||||
while ((sortingRight = averageZ[j]) < sortingMedian) j--;
|
||||
if (i <= j) {
|
||||
sortingChild = visibleChildren[i];
|
||||
visibleChildren[i] = visibleChildren[j];
|
||||
visibleChildren[j] = sortingChild;
|
||||
averageZ[i++] = sortingRight;
|
||||
averageZ[j--] = sortingLeft;
|
||||
}
|
||||
} while (i <= j);
|
||||
if (l < j) {
|
||||
sortingStack[sortingStackIndex++] = l;
|
||||
sortingStack[sortingStackIndex++] = j;
|
||||
}
|
||||
if (i < r) {
|
||||
sortingStack[sortingStackIndex++] = i;
|
||||
sortingStack[sortingStackIndex++] = r;
|
||||
}
|
||||
}
|
||||
// Отрисовка
|
||||
for (i = numVisibleChildren - 1; i >= 0; i--) {
|
||||
child = visibleChildren[i];
|
||||
if (camera.debugMode) child.debug(camera, child, canvas);
|
||||
child.draw(camera, child, canvas);
|
||||
visibleChildren[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
package alternativa.engine3d.containers {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.core.Object3DContainer;
|
||||
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class DirectionContainer extends Object3DContainer {
|
||||
|
||||
public var direction:Vector3D = new Vector3D(0, 0, -1);
|
||||
|
||||
override protected function drawVisibleChildren(camera:Camera3D, object:Object3D, canvas:Canvas):void {
|
||||
var i:int;
|
||||
var child:Object3D;
|
||||
if (Vector3D.Z_AXIS.dotProduct(object.cameraMatrix.deltaTransformVector(direction)) < 0) {
|
||||
for (i = 0; i < numVisibleChildren; i++) {
|
||||
child = visibleChildren[i];
|
||||
if (camera.debugMode) child.debug(camera, child, canvas);
|
||||
child.draw(camera, child, canvas);
|
||||
visibleChildren[i] = null;
|
||||
}
|
||||
} else {
|
||||
for (i = numVisibleChildren - 1; i >= 0; i--) {
|
||||
child = visibleChildren[i];
|
||||
if (camera.debugMode) child.debug(camera, child, canvas);
|
||||
child.draw(camera, child, canvas);
|
||||
visibleChildren[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
841
Alternativa3D7/7.0/alternativa/engine3d/containers/KDTree.as
Normal file
841
Alternativa3D7/7.0/alternativa/engine3d/containers/KDTree.as
Normal file
@@ -0,0 +1,841 @@
|
||||
package alternativa.engine3d.containers {
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Debug;
|
||||
import alternativa.engine3d.core.Geometry;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.objects.Occluder;
|
||||
import alternativa.engine3d.objects.Reference;
|
||||
|
||||
import flash.geom.Utils3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Контейнер, дочерние объекты которого помещены в бинарную древовидную структуру.
|
||||
* Для построения дерева нужно добавить статические дочерние объекты
|
||||
* с помощью addStaticChild(), затем вызвать createTree(). По баундам статических объектов
|
||||
* построится ориентированная по осям бинарная древовидная структура (KD - частный случай BSP).
|
||||
* Динамические объекты, можно в любое время добавлять addDynamicChild() и удалять removeDynamicChild().
|
||||
* Объекты, добавленные с помощью addChild() будут отрисовываться поверх всего в порядке добавления.
|
||||
*/
|
||||
public class KDTree extends ConflictContainer {
|
||||
|
||||
public var debugAlphaFade:Number = 0.8;
|
||||
|
||||
private var root:KDNode;
|
||||
|
||||
override alternativa3d function get canDraw():Boolean {
|
||||
return _numChildren > 0 || root != null;
|
||||
}
|
||||
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
// Если есть корневая нода
|
||||
if (root != null) {
|
||||
// Расчёт инверсной матрицы камеры и позицци камеры в контейнере
|
||||
calculateInverseCameraMatrix(object.cameraMatrix);
|
||||
// Расчёт плоскостей камеры в контейнере
|
||||
calculateCameraPlanes(camera);
|
||||
// Проверка на видимость рутовой ноды
|
||||
var culling:int = cullingInContainer(camera, root.boundBox, object.culling);
|
||||
if (culling >= 0) {
|
||||
// Подготовка канваса
|
||||
var canvas:Canvas = parentCanvas.getChildCanvas(false, true, object.alpha, object.blendMode, object.colorTransform, object.filters);
|
||||
canvas.numDraws = 0;
|
||||
// Окклюдеры
|
||||
numOccluders = 0;
|
||||
if (camera.numOccluders > 0) {
|
||||
updateOccluders(camera);
|
||||
}
|
||||
// Сбор видимой геометрии
|
||||
var geometry:Geometry = getGeometry(camera, object);
|
||||
var current:Geometry = geometry;
|
||||
while (current != null) {
|
||||
current.vertices.length = current.verticesLength;
|
||||
inverseCameraMatrix.transformVectors(current.vertices, current.vertices);
|
||||
current.calculateAABB();
|
||||
current = current.next;
|
||||
}
|
||||
// Отрисовка дерева
|
||||
drawNode(root, culling, camera, object, canvas, geometry);
|
||||
// Если была отрисовка
|
||||
if (canvas.numDraws > 0) {
|
||||
canvas.removeChildren(canvas.numDraws);
|
||||
} else {
|
||||
parentCanvas.numDraws--;
|
||||
}
|
||||
} else {
|
||||
super.draw(camera, object, parentCanvas);
|
||||
}
|
||||
} else {
|
||||
super.draw(camera, object, parentCanvas);
|
||||
}
|
||||
}
|
||||
|
||||
override alternativa3d function debug(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
var debugResult:int = camera.checkInDebug(this);
|
||||
if (debugResult == 0) return;
|
||||
var canvas:Canvas = parentCanvas.getChildCanvas(true, false);
|
||||
// Ноды
|
||||
if (debugResult & Debug.NODES) {
|
||||
if (root != null) {
|
||||
var culling:int = cullingInContainer(camera, root.boundBox, object.culling);
|
||||
if (culling >= 0) {
|
||||
debugNode(root, culling, camera, object, canvas, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Оси, центры, имена, баунды
|
||||
if (debugResult & Debug.AXES) object.drawAxes(camera, canvas);
|
||||
if (debugResult & Debug.CENTERS) object.drawCenter(camera, canvas);
|
||||
if (debugResult & Debug.NAMES) object.drawName(camera, canvas);
|
||||
if (debugResult & Debug.BOUNDS) object.drawBoundBox(camera, canvas);
|
||||
}
|
||||
|
||||
private function drawNode(node:KDNode, culling:int, camera:Camera3D, object:Object3D, canvas:Canvas, geometry:Geometry):void {
|
||||
var i:int;
|
||||
var next:Geometry;
|
||||
var negative:Geometry;
|
||||
var middle:Geometry;
|
||||
var positive:Geometry;
|
||||
var negativePart:Geometry;
|
||||
var positivePart:Geometry;
|
||||
if (camera.occludedAll) {
|
||||
while (geometry != null) {
|
||||
next = geometry.next;
|
||||
geometry.destroy();
|
||||
geometry = next;
|
||||
}
|
||||
return;
|
||||
}
|
||||
var nodeObjects:Vector.<Object3D> = node.objects;
|
||||
var nodeNumObjects:int = node.numObjects;
|
||||
var nodeNumNonOccluders:int = node.numNonOccluders;
|
||||
var staticChild:Object3D;
|
||||
// Узловая нода
|
||||
if (node.negative != null) {
|
||||
var negativeCulling:int = (culling > 0 || numOccluders > 0) ? cullingInContainer(camera, node.negative.boundBox, culling) : 0;
|
||||
var positiveCulling:int = (culling > 0 || numOccluders > 0) ? cullingInContainer(camera, node.positive.boundBox, culling) : 0;
|
||||
var axisX:Boolean = node.axis == 0;
|
||||
var axisY:Boolean = node.axis == 1;
|
||||
var min:Number;
|
||||
var max:Number;
|
||||
// Если видны обе дочерние ноды
|
||||
if (negativeCulling >= 0 && positiveCulling >= 0) {
|
||||
// Перебор динамиков
|
||||
while (geometry != null) {
|
||||
next = geometry.next;
|
||||
// Проверка с окклюдерами
|
||||
if (geometry.numOccluders < numOccluders && occludeGeometry(camera, geometry)) {
|
||||
geometry.destroy();
|
||||
} else {
|
||||
min = axisX ? geometry.minX : (axisY ? geometry.minY : geometry.minZ);
|
||||
max = axisX ? geometry.maxX : (axisY ? geometry.maxY : geometry.maxZ);
|
||||
if (max <= node.maxCoord) {
|
||||
if (min < node.minCoord) {
|
||||
geometry.next = negative;
|
||||
negative = geometry;
|
||||
} else {
|
||||
geometry.next = middle;
|
||||
middle = geometry;
|
||||
}
|
||||
} else if (min >= node.minCoord) {
|
||||
geometry.next = positive;
|
||||
positive = geometry;
|
||||
} else {
|
||||
negativePart = geometry.create();
|
||||
positivePart = geometry.create();
|
||||
geometry.split(axisX, axisY, node.coord, threshold, negativePart, positivePart);
|
||||
geometry.destroy();
|
||||
// Если негативный не пустой
|
||||
if (negativePart.fragment != null) {
|
||||
negativePart.next = negative;
|
||||
negative = negativePart;
|
||||
} else {
|
||||
negativePart.destroy();
|
||||
}
|
||||
// Если позитивный не пустой
|
||||
if (positivePart.fragment != null) {
|
||||
positivePart.next = positive;
|
||||
positive = positivePart;
|
||||
} else {
|
||||
positivePart.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
geometry = next;
|
||||
}
|
||||
// Отрисовка дочерних нод и объектов в плоскости
|
||||
if (axisX && cameraX > node.coord || axisY && cameraY > node.coord || !axisX && !axisY && cameraZ > node.coord) {
|
||||
// Отрисовка позитивной ноды
|
||||
drawNode(node.positive, positiveCulling, camera, object, canvas, positive);
|
||||
// Отрисовка динамических объектов в ноде
|
||||
while (middle != null) {
|
||||
next = middle.next;
|
||||
// Проверка с окклюдерами и отрисовка
|
||||
if (middle.numOccluders >= numOccluders || !occludeGeometry(camera, middle)) {
|
||||
if (camera.debugMode) middle.debug(camera, object, canvas, threshold, 1, object.cameraMatrix);
|
||||
middle.draw(camera, canvas, threshold, object.cameraMatrix);
|
||||
}
|
||||
middle.destroy();
|
||||
middle = next;
|
||||
}
|
||||
// Отрисовка плоских статических объектов в ноде
|
||||
for (i = 0; i < nodeNumObjects; i++) {
|
||||
staticChild = nodeObjects[i];
|
||||
if (staticChild.visible && staticChild.canDraw && ((staticChild.culling = culling) == 0 && numOccluders == 0 || (staticChild.culling = cullingInContainer(camera, node.bounds[i], culling)) >= 0)) {
|
||||
staticChild.cameraMatrix.identity();
|
||||
staticChild.cameraMatrix.prepend(object.cameraMatrix);
|
||||
staticChild.cameraMatrix.prepend(staticChild.matrix);
|
||||
if (camera.debugMode) staticChild.debug(camera, staticChild, canvas);
|
||||
staticChild.draw(camera, staticChild, canvas);
|
||||
}
|
||||
}
|
||||
// Обновление окклюдеров
|
||||
if (nodeNumObjects > nodeNumNonOccluders) {
|
||||
updateOccluders(camera);
|
||||
}
|
||||
// Отрисовка негативной ноды
|
||||
drawNode(node.negative, negativeCulling, camera, object, canvas, negative);
|
||||
} else {
|
||||
// Отрисовка негативной ноды
|
||||
drawNode(node.negative, negativeCulling, camera, object, canvas, negative);
|
||||
// Отрисовка динамических объектов в ноде
|
||||
while (middle != null) {
|
||||
next = middle.next;
|
||||
// Проверка с окклюдерами и отрисовка
|
||||
if (middle.numOccluders >= numOccluders || !occludeGeometry(camera, middle)) {
|
||||
if (camera.debugMode) middle.debug(camera, object, canvas, threshold, 1, object.cameraMatrix);
|
||||
middle.draw(camera, canvas, threshold, object.cameraMatrix);
|
||||
}
|
||||
middle.destroy();
|
||||
middle = next;
|
||||
}
|
||||
// Отрисовка статических объектов в ноде
|
||||
for (i = 0; i < nodeNumObjects; i++) {
|
||||
staticChild = nodeObjects[i];
|
||||
if (staticChild.visible && staticChild.canDraw && ((staticChild.culling = culling) == 0 && numOccluders == 0 || (staticChild.culling = cullingInContainer(camera, node.bounds[i], culling)) >= 0)) {
|
||||
staticChild.cameraMatrix.identity();
|
||||
staticChild.cameraMatrix.prepend(object.cameraMatrix);
|
||||
staticChild.cameraMatrix.prepend(staticChild.matrix);
|
||||
if (camera.debugMode) staticChild.debug(camera, staticChild, canvas);
|
||||
staticChild.draw(camera, staticChild, canvas);
|
||||
}
|
||||
}
|
||||
// Обновление окклюдеров
|
||||
if (nodeNumObjects > nodeNumNonOccluders) {
|
||||
updateOccluders(camera);
|
||||
}
|
||||
// Отрисовка позитивной ноды
|
||||
drawNode(node.positive, positiveCulling, camera, object, canvas, positive);
|
||||
}
|
||||
// Если видна только негативная
|
||||
} else if (negativeCulling >= 0) {
|
||||
// Перебор динамиков
|
||||
while (geometry != null) {
|
||||
next = geometry.next;
|
||||
// Проверка с окклюдерами
|
||||
if (geometry.numOccluders < numOccluders && occludeGeometry(camera, geometry)) {
|
||||
geometry.destroy();
|
||||
} else {
|
||||
min = axisX ? geometry.minX : (axisY ? geometry.minY : geometry.minZ);
|
||||
max = axisX ? geometry.maxX : (axisY ? geometry.maxY : geometry.maxZ);
|
||||
if (max <= node.maxCoord) {
|
||||
geometry.next = negative;
|
||||
negative = geometry;
|
||||
} else if (min >= node.minCoord) {
|
||||
geometry.destroy();
|
||||
} else {
|
||||
negativePart = geometry.create();
|
||||
geometry.split(axisX, axisY, node.coord, threshold, negativePart, null);
|
||||
geometry.destroy();
|
||||
// Если негативный не пустой
|
||||
if (negativePart.fragment != null) {
|
||||
negativePart.next = negative;
|
||||
negative = negativePart;
|
||||
} else {
|
||||
negativePart.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
geometry = next;
|
||||
}
|
||||
// Отрисовка негативной ноды
|
||||
drawNode(node.negative, negativeCulling, camera, object, canvas, negative);
|
||||
// Если видна только позитивная
|
||||
} else if (positiveCulling >= 0) {
|
||||
// Перебор динамиков
|
||||
while (geometry != null) {
|
||||
next = geometry.next;
|
||||
// Проверка с окклюдерами
|
||||
if (geometry.numOccluders < numOccluders && occludeGeometry(camera, geometry)) {
|
||||
geometry.destroy();
|
||||
} else {
|
||||
min = axisX ? geometry.minX : (axisY ? geometry.minY : geometry.minZ);
|
||||
max = axisX ? geometry.maxX : (axisY ? geometry.maxY : geometry.maxZ);
|
||||
|
||||
if (max <= node.maxCoord) {
|
||||
geometry.destroy();
|
||||
} else if (min >= node.minCoord) {
|
||||
geometry.next = positive;
|
||||
positive = geometry;
|
||||
} else {
|
||||
positivePart = geometry.create();
|
||||
geometry.split(axisX, axisY, node.coord, threshold, null, positivePart);
|
||||
geometry.destroy();
|
||||
// Если позитивный не пустой
|
||||
if (positivePart.fragment != null) {
|
||||
positivePart.next = positive;
|
||||
positive = positivePart;
|
||||
} else {
|
||||
positivePart.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
geometry = next;
|
||||
}
|
||||
// Отрисовка позитивной ноды
|
||||
drawNode(node.positive, positiveCulling, camera, object, canvas, positive);
|
||||
// Если обе ноды не видны
|
||||
} else {
|
||||
// Уничтожение динамиков
|
||||
while (geometry != null) {
|
||||
next = geometry.next;
|
||||
geometry.destroy();
|
||||
geometry = next;
|
||||
}
|
||||
}
|
||||
// Конечная нода
|
||||
} else {
|
||||
// Если есть статические объекты, не считая окклюдеры
|
||||
if (nodeNumNonOccluders > 0) {
|
||||
// Если есть конфликт
|
||||
if (nodeNumNonOccluders > 1 || geometry != null) {
|
||||
// Перебор динамиков
|
||||
while (geometry != null) {
|
||||
next = geometry.next;
|
||||
// Проверка с окклюдерами
|
||||
if (geometry.numOccluders < numOccluders && occludeGeometry(camera, geometry)) {
|
||||
geometry.destroy();
|
||||
} else {
|
||||
geometry.vertices.length = geometry.verticesLength;
|
||||
object.cameraMatrix.transformVectors(geometry.vertices, geometry.vertices);
|
||||
geometry.next = middle;
|
||||
middle = geometry;
|
||||
}
|
||||
geometry = next;
|
||||
}
|
||||
// Превращение статиков в геометрию
|
||||
for (i = 0; i < nodeNumNonOccluders; i++) {
|
||||
staticChild = nodeObjects[i];
|
||||
if (staticChild.visible && staticChild.canDraw && ((staticChild.culling = culling) == 0 && numOccluders == 0 || (staticChild.culling = cullingInContainer(camera, node.bounds[i], culling)) >= 0)) {
|
||||
staticChild.cameraMatrix.identity();
|
||||
staticChild.cameraMatrix.prepend(object.cameraMatrix);
|
||||
staticChild.cameraMatrix.prepend(staticChild.matrix);
|
||||
geometry = staticChild.getGeometry(camera, staticChild);
|
||||
while (geometry != null) {
|
||||
next = geometry.next;
|
||||
geometry.next = middle;
|
||||
middle = geometry;
|
||||
geometry = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Разруливаем конфликт
|
||||
if (middle != null) {
|
||||
if (middle.next != null) {
|
||||
drawConflictGeometry(camera, object, canvas, middle);
|
||||
} else {
|
||||
if (camera.debugMode) middle.debug(camera, object, canvas, threshold, 1);
|
||||
middle.draw(camera, canvas, threshold);
|
||||
middle.destroy();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Если только один статик
|
||||
staticChild = nodeObjects[i];
|
||||
if (staticChild.visible && staticChild.canDraw) {
|
||||
staticChild.cameraMatrix.identity();
|
||||
staticChild.cameraMatrix.prepend(object.cameraMatrix);
|
||||
staticChild.cameraMatrix.prepend(staticChild.matrix);
|
||||
staticChild.culling = culling;
|
||||
if (camera.debugMode) staticChild.debug(camera, staticChild, canvas);
|
||||
staticChild.draw(camera, staticChild, canvas);
|
||||
}
|
||||
}
|
||||
// Если нет статических объектов
|
||||
} else {
|
||||
// Если есть динамические объекты
|
||||
if (geometry != null) {
|
||||
// Если динамических объектов несколько
|
||||
if (geometry.next != null) {
|
||||
// Если есть окклюдеры
|
||||
if (numOccluders > 0) {
|
||||
// Перебор динамиков
|
||||
while (geometry != null) {
|
||||
next = geometry.next;
|
||||
// Проверка с окклюдерами
|
||||
if (geometry.numOccluders < numOccluders && occludeGeometry(camera, geometry)) {
|
||||
geometry.destroy();
|
||||
} else {
|
||||
geometry.next = middle;
|
||||
middle = geometry;
|
||||
}
|
||||
geometry = next;
|
||||
}
|
||||
// Если остались объекты
|
||||
if (middle != null) {
|
||||
if (middle.next != null) {
|
||||
// Разруливание
|
||||
if (resolveByAABB) {
|
||||
drawAABBGeometry(camera, object, canvas, middle);
|
||||
} else if (resolveByOOBB) {
|
||||
geometry = middle;
|
||||
while (geometry != null) {
|
||||
geometry.vertices.length = geometry.verticesLength;
|
||||
object.cameraMatrix.transformVectors(geometry.vertices, geometry.vertices);
|
||||
if (!geometry.viewAligned) {
|
||||
geometry.calculateOOBB();
|
||||
}
|
||||
geometry = geometry.next;
|
||||
}
|
||||
drawOOBBGeometry(camera, object, canvas, middle);
|
||||
} else {
|
||||
geometry = middle;
|
||||
while (geometry != null) {
|
||||
geometry.vertices.length = geometry.verticesLength;
|
||||
object.cameraMatrix.transformVectors(geometry.vertices, geometry.vertices);
|
||||
geometry = geometry.next;
|
||||
}
|
||||
drawConflictGeometry(camera, object, canvas, middle);
|
||||
}
|
||||
} else {
|
||||
if (camera.debugMode) middle.debug(camera, object, canvas, threshold, 1, object.cameraMatrix);
|
||||
middle.draw(camera, canvas, threshold, object.cameraMatrix);
|
||||
middle.destroy();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Разруливание
|
||||
middle = geometry;
|
||||
if (resolveByAABB) {
|
||||
drawAABBGeometry(camera, object, canvas, middle);
|
||||
} else if (resolveByOOBB) {
|
||||
geometry = middle;
|
||||
while (geometry != null) {
|
||||
geometry.vertices.length = geometry.verticesLength;
|
||||
object.cameraMatrix.transformVectors(geometry.vertices, geometry.vertices);
|
||||
if (!geometry.viewAligned) {
|
||||
geometry.calculateOOBB();
|
||||
}
|
||||
geometry = geometry.next;
|
||||
}
|
||||
drawOOBBGeometry(camera, object, canvas, middle);
|
||||
} else {
|
||||
geometry = middle;
|
||||
while (geometry != null) {
|
||||
geometry.vertices.length = geometry.verticesLength;
|
||||
object.cameraMatrix.transformVectors(geometry.vertices, geometry.vertices);
|
||||
geometry = geometry.next;
|
||||
}
|
||||
drawConflictGeometry(camera, object, canvas, middle);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Проверка с окклюдерами и отрисовка
|
||||
if (geometry.numOccluders >= numOccluders || !occludeGeometry(camera, geometry)) {
|
||||
if (camera.debugMode) geometry.debug(camera, object, canvas, threshold, 1, object.cameraMatrix);
|
||||
geometry.draw(camera, canvas, threshold, object.cameraMatrix);
|
||||
}
|
||||
geometry.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Если в ноде есть окклюдеры
|
||||
if (nodeNumObjects > nodeNumNonOccluders) {
|
||||
for (i = nodeNumNonOccluders; i < nodeNumObjects; i++) {
|
||||
staticChild = nodeObjects[i];
|
||||
if (staticChild.visible && staticChild.canDraw && ((staticChild.culling = culling) == 0 && numOccluders == 0 || (staticChild.culling = cullingInContainer(camera, node.bounds[i], culling)) >= 0)) {
|
||||
staticChild.cameraMatrix.identity();
|
||||
staticChild.cameraMatrix.prepend(object.cameraMatrix);
|
||||
staticChild.cameraMatrix.prepend(staticChild.matrix);
|
||||
if (camera.debugMode) staticChild.debug(camera, staticChild, canvas);
|
||||
staticChild.draw(camera, staticChild, canvas);
|
||||
}
|
||||
}
|
||||
// Обновление окклюдеров
|
||||
updateOccluders(camera);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static private const nodeVertices:Vector.<Number> = new Vector.<Number>(12, true);
|
||||
static private const nodeProjectedVertices:Vector.<Number> = new Vector.<Number>(8, true);
|
||||
static private const nodeUVTs:Vector.<Number> = new Vector.<Number>(12, true);
|
||||
|
||||
private function debugNode(node:KDNode, culling:int, camera:Camera3D, object:Object3D, canvas:Canvas, alpha:Number):void {
|
||||
if (node.negative != null) {
|
||||
var negativeCulling:int = (culling > 0) ? cullingInContainer(camera, node.negative.boundBox, culling) : 0;
|
||||
var positiveCulling:int = (culling > 0) ? cullingInContainer(camera, node.positive.boundBox, culling) : 0;
|
||||
if (negativeCulling >= 0) {
|
||||
debugNode(node.negative, negativeCulling, camera, object, canvas, alpha*debugAlphaFade);
|
||||
}
|
||||
if (positiveCulling >= 0) {
|
||||
debugNode(node.positive, positiveCulling, camera, object, canvas, alpha*debugAlphaFade);
|
||||
}
|
||||
|
||||
if (node.axis == 0) {
|
||||
nodeVertices[0] = node.coord;
|
||||
nodeVertices[1] = node.boundBox.minY;
|
||||
nodeVertices[2] = node.boundBox.maxZ;
|
||||
|
||||
nodeVertices[3] = node.coord;
|
||||
nodeVertices[4] = node.boundBox.maxY;
|
||||
nodeVertices[5] = node.boundBox.maxZ;
|
||||
|
||||
nodeVertices[6] = node.coord;
|
||||
nodeVertices[7] = node.boundBox.maxY;
|
||||
nodeVertices[8] = node.boundBox.minZ;
|
||||
|
||||
nodeVertices[9] = node.coord;
|
||||
nodeVertices[10] = node.boundBox.minY;
|
||||
nodeVertices[11] = node.boundBox.minZ;
|
||||
} else if (node.axis == 1) {
|
||||
nodeVertices[0] = node.boundBox.maxX;
|
||||
nodeVertices[1] = node.coord;
|
||||
nodeVertices[2] = node.boundBox.maxZ;
|
||||
|
||||
nodeVertices[3] = node.boundBox.minX;
|
||||
nodeVertices[4] = node.coord;
|
||||
nodeVertices[5] = node.boundBox.maxZ;
|
||||
|
||||
nodeVertices[6] = node.boundBox.minX;
|
||||
nodeVertices[7] = node.coord;
|
||||
nodeVertices[8] = node.boundBox.minZ;
|
||||
|
||||
nodeVertices[9] = node.boundBox.maxX;
|
||||
nodeVertices[10] = node.coord;
|
||||
nodeVertices[11] = node.boundBox.minZ;
|
||||
} else {
|
||||
nodeVertices[0] = node.boundBox.minX;
|
||||
nodeVertices[1] = node.boundBox.minY;
|
||||
nodeVertices[2] = node.coord;
|
||||
|
||||
nodeVertices[3] = node.boundBox.maxX;
|
||||
nodeVertices[4] = node.boundBox.minY;
|
||||
nodeVertices[5] = node.coord;
|
||||
|
||||
nodeVertices[6] = node.boundBox.maxX;
|
||||
nodeVertices[7] = node.boundBox.maxY;
|
||||
nodeVertices[8] = node.coord;
|
||||
|
||||
nodeVertices[9] = node.boundBox.minX;
|
||||
nodeVertices[10] = node.boundBox.maxY;
|
||||
nodeVertices[11] = node.coord;
|
||||
}
|
||||
object.cameraMatrix.transformVectors(nodeVertices, nodeVertices);
|
||||
var i:int;
|
||||
for (i = 0; i < 12; i += 3) {
|
||||
if (nodeVertices[int(i + 2)] <= 0) break;
|
||||
}
|
||||
if (i == 12) {
|
||||
Utils3D.projectVectors(camera.projectionMatrix, nodeVertices, nodeProjectedVertices, nodeUVTs);
|
||||
canvas.gfx.lineStyle(0, (node.axis == 0) ? 0xFF0000 : ((node.axis == 1) ? 0x00FF00 : 0x0000FF));
|
||||
canvas.gfx.moveTo(nodeProjectedVertices[0], nodeProjectedVertices[1]);
|
||||
canvas.gfx.lineTo(nodeProjectedVertices[2], nodeProjectedVertices[3]);
|
||||
canvas.gfx.lineTo(nodeProjectedVertices[4], nodeProjectedVertices[5]);
|
||||
canvas.gfx.lineTo(nodeProjectedVertices[6], nodeProjectedVertices[7]);
|
||||
canvas.gfx.lineTo(nodeProjectedVertices[0], nodeProjectedVertices[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function createTree(staticObjects:Vector.<Object3D>, boundBox:BoundBox = null):void {
|
||||
var numStaticChildren:int = staticObjects.length;
|
||||
if (numStaticChildren > 0) {
|
||||
// Создаём корневую ноду
|
||||
root = new KDNode();
|
||||
root.objects = new Vector.<Object3D>();
|
||||
root.bounds = new Vector.<BoundBox>();
|
||||
root.numObjects = 0;
|
||||
// Расчитываем баунды объектов и рутовой ноды
|
||||
root.boundBox = (boundBox != null) ? boundBox : new BoundBox();
|
||||
// Сначала добавляем не окклюдеры
|
||||
var staticOccluders:Vector.<Object3D> = new Vector.<Object3D>();
|
||||
var staticOccludersLength:int = 0;
|
||||
var object:Object3D;
|
||||
var objectBoundBox:BoundBox;
|
||||
for (var i:int = 0; i < numStaticChildren; i++) {
|
||||
object = staticObjects[i];
|
||||
// Поиск оригинального объекта
|
||||
var source:Object3D = object;
|
||||
while (source is Reference) {
|
||||
source = (source as Reference).referenceObject;
|
||||
}
|
||||
// Если окклюдер
|
||||
if (source is Occluder) {
|
||||
staticOccluders[staticOccludersLength++] = object;
|
||||
} else {
|
||||
objectBoundBox = object.calculateBoundBox(object.matrix);
|
||||
root.objects[root.numObjects] = object;
|
||||
root.bounds[root.numObjects++] = objectBoundBox;
|
||||
root.boundBox.addBoundBox(objectBoundBox);
|
||||
}
|
||||
}
|
||||
// Добавляем окклюдеры
|
||||
for (i = 0; i < staticOccludersLength; i++) {
|
||||
object = staticOccluders[i];
|
||||
objectBoundBox = object.calculateBoundBox(object.matrix);
|
||||
root.objects[root.numObjects] = object;
|
||||
root.bounds[root.numObjects++] = objectBoundBox;
|
||||
root.boundBox.addBoundBox(objectBoundBox);
|
||||
}
|
||||
// Разделяем рутовую ноду
|
||||
splitNode(root);
|
||||
}
|
||||
}
|
||||
|
||||
private var splitAxis:int;
|
||||
private var splitCoord:Number;
|
||||
private var splitCost:Number;
|
||||
static private const nodeBoundBoxThreshold:BoundBox = new BoundBox();
|
||||
static private const splitCoordsX:Vector.<Number> = new Vector.<Number>();
|
||||
static private const splitCoordsY:Vector.<Number> = new Vector.<Number>();
|
||||
static private const splitCoordsZ:Vector.<Number> = new Vector.<Number>();
|
||||
private function splitNode(node:KDNode):void {
|
||||
|
||||
var object:Object3D, boundBox:BoundBox, i:int, j:int, k:int, c1:Number, c2:Number, coordMin:Number, coordMax:Number, area:Number, areaNegative:Number, areaPositive:Number, numNegative:int, numPositive:int, conflict:Boolean, cost:Number;
|
||||
var nodeBoundBox:BoundBox = node.boundBox;
|
||||
|
||||
// Подготовка баунда с погрешностями
|
||||
nodeBoundBoxThreshold.minX = nodeBoundBox.minX + threshold;
|
||||
nodeBoundBoxThreshold.minY = nodeBoundBox.minY + threshold;
|
||||
nodeBoundBoxThreshold.minZ = nodeBoundBox.minZ + threshold;
|
||||
nodeBoundBoxThreshold.maxX = nodeBoundBox.maxX - threshold;
|
||||
nodeBoundBoxThreshold.maxY = nodeBoundBox.maxY - threshold;
|
||||
nodeBoundBoxThreshold.maxZ = nodeBoundBox.maxZ - threshold;
|
||||
var doubleThreshold:Number = threshold + threshold;
|
||||
|
||||
// Собираем опорные координаты
|
||||
var numSplitCoordsX:int = 0, numSplitCoordsY:int = 0, numSplitCoordsZ:int = 0;
|
||||
for (i = 0; i < node.numObjects; i++) {
|
||||
boundBox = node.bounds[i];
|
||||
if (boundBox.maxX - boundBox.minX <= doubleThreshold) {
|
||||
if (boundBox.minX <= nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = nodeBoundBox.minX;
|
||||
else if (boundBox.maxX >= nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = nodeBoundBox.maxX;
|
||||
else splitCoordsX[numSplitCoordsX++] = (boundBox.minX + boundBox.maxX)*0.5;
|
||||
} else {
|
||||
if (boundBox.minX > nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = boundBox.minX;
|
||||
if (boundBox.maxX < nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = boundBox.maxX;
|
||||
}
|
||||
if (boundBox.maxY - boundBox.minY <= doubleThreshold) {
|
||||
if (boundBox.minY <= nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = nodeBoundBox.minY;
|
||||
else if (boundBox.maxY >= nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = nodeBoundBox.maxY;
|
||||
else splitCoordsY[numSplitCoordsY++] = (boundBox.minY + boundBox.maxY)*0.5;
|
||||
} else {
|
||||
if (boundBox.minY > nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = boundBox.minY;
|
||||
if (boundBox.maxY < nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = boundBox.maxY;
|
||||
}
|
||||
if (boundBox.maxZ - boundBox.minZ <= doubleThreshold) {
|
||||
if (boundBox.minZ <= nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = nodeBoundBox.minZ;
|
||||
else if (boundBox.maxZ >= nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = nodeBoundBox.maxZ;
|
||||
else splitCoordsZ[numSplitCoordsZ++] = (boundBox.minZ + boundBox.maxZ)*0.5;
|
||||
} else {
|
||||
if (boundBox.minZ > nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.minZ;
|
||||
if (boundBox.maxZ < nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.maxZ;
|
||||
}
|
||||
}
|
||||
|
||||
// Убираем дубликаты координат, ищем наилучший сплит
|
||||
splitAxis = -1; splitCost = Number.MAX_VALUE;
|
||||
i = 0; area = (nodeBoundBox.maxY - nodeBoundBox.minY)*(nodeBoundBox.maxZ - nodeBoundBox.minZ);
|
||||
while (i < numSplitCoordsX) {
|
||||
if (!isNaN(c1 = splitCoordsX[i++])) {
|
||||
coordMin = c1 - threshold;
|
||||
coordMax = c1 + threshold;
|
||||
areaNegative = area*(c1 - nodeBoundBox.minX);
|
||||
areaPositive = area*(nodeBoundBox.maxX - c1);
|
||||
numNegative = numPositive = 0;
|
||||
conflict = false;
|
||||
// Проверяем объекты
|
||||
for (j = 0; j < node.numObjects; j++) {
|
||||
boundBox = node.bounds[j];
|
||||
if (boundBox.maxX <= coordMax) {
|
||||
if (boundBox.minX < coordMin) numNegative++;
|
||||
} else {
|
||||
if (boundBox.minX >= coordMin) numPositive++; else {conflict = true; break;}
|
||||
}
|
||||
}
|
||||
// Если хороший сплит, сохраняем
|
||||
if (!conflict && (cost = areaNegative*numNegative + areaPositive*numPositive) < splitCost) {
|
||||
splitCost = cost;
|
||||
splitAxis = 0;
|
||||
splitCoord = c1;
|
||||
}
|
||||
j = i;
|
||||
while (++j < numSplitCoordsX) if ((c2 = splitCoordsX[j]) >= c1 - threshold && c2 <= c1 + threshold) splitCoordsX[j] = NaN;
|
||||
}
|
||||
}
|
||||
i = 0; area = (nodeBoundBox.maxX - nodeBoundBox.minX)*(nodeBoundBox.maxZ - nodeBoundBox.minZ);
|
||||
while (i < numSplitCoordsY) {
|
||||
if (!isNaN(c1 = splitCoordsY[i++])) {
|
||||
coordMin = c1 - threshold;
|
||||
coordMax = c1 + threshold;
|
||||
areaNegative = area*(c1 - nodeBoundBox.minY);
|
||||
areaPositive = area*(nodeBoundBox.maxY - c1);
|
||||
numNegative = numPositive = 0;
|
||||
conflict = false;
|
||||
// Проверяем объекты
|
||||
for (j = 0; j < node.numObjects; j++) {
|
||||
boundBox = node.bounds[j];
|
||||
if (boundBox.maxY <= coordMax) {
|
||||
if (boundBox.minY < coordMin) numNegative++;
|
||||
} else {
|
||||
if (boundBox.minY >= coordMin) numPositive++; else {conflict = true; break;}
|
||||
}
|
||||
}
|
||||
// Если хороший сплит, сохраняем
|
||||
if (!conflict && (cost = areaNegative*numNegative + areaPositive*numPositive) < splitCost) {
|
||||
splitCost = cost;
|
||||
splitAxis = 1;
|
||||
splitCoord = c1;
|
||||
}
|
||||
j = i;
|
||||
while (++j < numSplitCoordsY) if ((c2 = splitCoordsY[j]) >= c1 - threshold && c2 <= c1 + threshold) splitCoordsY[j] = NaN;
|
||||
}
|
||||
}
|
||||
i = 0; area = (nodeBoundBox.maxX - nodeBoundBox.minX)*(nodeBoundBox.maxY - nodeBoundBox.minY);
|
||||
while (i < numSplitCoordsZ) {
|
||||
if (!isNaN(c1 = splitCoordsZ[i++])) {
|
||||
coordMin = c1 - threshold;
|
||||
coordMax = c1 + threshold;
|
||||
areaNegative = area*(c1 - nodeBoundBox.minZ);
|
||||
areaPositive = area*(nodeBoundBox.maxZ - c1);
|
||||
numNegative = numPositive = 0;
|
||||
conflict = false;
|
||||
// Проверяем объекты
|
||||
for (j = 0; j < node.numObjects; j++) {
|
||||
boundBox = node.bounds[j];
|
||||
if (boundBox.maxZ <= coordMax) {
|
||||
if (boundBox.minZ < coordMin) numNegative++;
|
||||
} else {
|
||||
if (boundBox.minZ >= coordMin) numPositive++; else {conflict = true; break;}
|
||||
}
|
||||
}
|
||||
// Если хороший сплит, сохраняем
|
||||
if (!conflict && (cost = areaNegative*numNegative + areaPositive*numPositive) < splitCost) {
|
||||
splitCost = cost;
|
||||
splitAxis = 2;
|
||||
splitCoord = c1;
|
||||
}
|
||||
j = i;
|
||||
while (++j < numSplitCoordsZ) if ((c2 = splitCoordsZ[j]) >= c1 - threshold && c2 <= c1 + threshold) splitCoordsZ[j] = NaN;
|
||||
}
|
||||
}
|
||||
|
||||
// Если сплит не найден, выходим
|
||||
if (splitAxis < 0) {
|
||||
// Находим, откуда начинаются окклюдеры
|
||||
for (i = 0; i < node.numObjects; i++) {
|
||||
object = node.objects[i];
|
||||
// Поиск оригинального объекта
|
||||
while (object is Reference) object = (object as Reference).referenceObject;
|
||||
if (object is Occluder) break;
|
||||
}
|
||||
node.numNonOccluders = i;
|
||||
return;
|
||||
}
|
||||
|
||||
// Разделяем ноду
|
||||
var axisX:Boolean = splitAxis == 0, axisY:Boolean = splitAxis == 1;
|
||||
node.axis = splitAxis;
|
||||
node.coord = splitCoord;
|
||||
node.minCoord = coordMin = splitCoord - threshold;
|
||||
node.maxCoord = coordMax = splitCoord + threshold;
|
||||
|
||||
// Создаём дочерние ноды
|
||||
node.negative = new KDNode();
|
||||
node.positive = new KDNode();
|
||||
node.negative.boundBox = nodeBoundBox.clone();
|
||||
node.positive.boundBox = nodeBoundBox.clone();
|
||||
node.negative.numObjects = 0;
|
||||
node.positive.numObjects = 0;
|
||||
if (axisX) node.negative.boundBox.maxX = node.positive.boundBox.minX = splitCoord;
|
||||
else if (axisY) node.negative.boundBox.maxY = node.positive.boundBox.minY = splitCoord;
|
||||
else node.negative.boundBox.maxZ = node.positive.boundBox.minZ = splitCoord;
|
||||
|
||||
// Распределяем объекты по дочерним нодам
|
||||
for (i = 0; i < node.numObjects; i++) {
|
||||
object = node.objects[i];
|
||||
boundBox = node.bounds[i];
|
||||
var min:Number = axisX ? boundBox.minX : (axisY ? boundBox.minY : boundBox.minZ);
|
||||
var max:Number = axisX ? boundBox.maxX : (axisY ? boundBox.maxY : boundBox.maxZ);
|
||||
if (max <= coordMax) {
|
||||
if (min < coordMin) {
|
||||
// Объект в негативной стороне
|
||||
if (node.negative.objects == null) node.negative.objects = new Vector.<Object3D>(), node.negative.bounds = new Vector.<BoundBox>();
|
||||
node.negative.objects[node.negative.numObjects] = object, node.negative.bounds[node.negative.numObjects++] = boundBox;
|
||||
node.objects[i] = null, node.bounds[i] = null;
|
||||
} else {
|
||||
// Остаётся в ноде
|
||||
}
|
||||
} else {
|
||||
if (min >= coordMin) {
|
||||
// Объект в положительной стороне
|
||||
if (node.positive.objects == null) node.positive.objects = new Vector.<Object3D>(), node.positive.bounds = new Vector.<BoundBox>();
|
||||
node.positive.objects[node.positive.numObjects] = object, node.positive.bounds[node.positive.numObjects++] = boundBox;
|
||||
node.objects[i] = null, node.bounds[i] = null;
|
||||
} else {
|
||||
// Распилился
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Очистка списка объектов
|
||||
for (i = 0, j = 0; i < node.numObjects; i++) if (node.objects[i] != null) node.objects[j] = node.objects[i], node.bounds[j++] = node.bounds[i];
|
||||
if (j > 0) {
|
||||
node.numObjects = node.objects.length = node.bounds.length = j;
|
||||
// Находим, откуда начинаются окклюдеры
|
||||
for (i = 0; i < node.numObjects; i++) {
|
||||
object = node.objects[i];
|
||||
// Поиск оригинального объекта
|
||||
while (object is Reference) object = (object as Reference).referenceObject;
|
||||
if (object is Occluder) break;
|
||||
}
|
||||
node.numNonOccluders = i;
|
||||
} else {
|
||||
node.numObjects = node.numNonOccluders = 0, node.objects = null, node.bounds = null;
|
||||
}
|
||||
|
||||
// Разделение дочерних нод
|
||||
if (node.negative.objects != null) splitNode(node.negative);
|
||||
if (node.positive.objects != null) splitNode(node.positive);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
class KDNode {
|
||||
|
||||
public var positive:KDNode;
|
||||
public var negative:KDNode;
|
||||
|
||||
public var axis:int;
|
||||
public var coord:Number;
|
||||
public var minCoord:Number;
|
||||
public var maxCoord:Number;
|
||||
|
||||
public var boundBox:BoundBox;
|
||||
|
||||
public var objects:Vector.<Object3D>;
|
||||
public var bounds:Vector.<BoundBox>;
|
||||
|
||||
public var numObjects:int = 0;
|
||||
public var numNonOccluders:int = 0;
|
||||
|
||||
}
|
||||
138
Alternativa3D7/7.0/alternativa/engine3d/containers/SkyBox.as
Normal file
138
Alternativa3D7/7.0/alternativa/engine3d/containers/SkyBox.as
Normal file
@@ -0,0 +1,138 @@
|
||||
package alternativa.engine3d.containers {
|
||||
import alternativa.engine3d.core.Object3DContainer;
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
import flash.display.BitmapData;
|
||||
|
||||
public class SkyBox extends Object3DContainer {
|
||||
|
||||
private var backPlane:Mesh;
|
||||
private var frontPlane:Mesh;
|
||||
private var topPlane:Mesh;
|
||||
private var bottomPlane:Mesh;
|
||||
private var leftPlane:Mesh;
|
||||
private var rightPlane:Mesh;
|
||||
|
||||
public function SkyBox(size:Number = 1000):void {
|
||||
backPlane = new Mesh();
|
||||
frontPlane = new Mesh();
|
||||
topPlane = new Mesh();
|
||||
bottomPlane = new Mesh();
|
||||
leftPlane = new Mesh();
|
||||
rightPlane = new Mesh();
|
||||
|
||||
backPlane.clipping = 2;
|
||||
frontPlane.clipping = 2;
|
||||
topPlane.clipping = 2;
|
||||
bottomPlane.clipping = 2;
|
||||
leftPlane.clipping = 2;
|
||||
rightPlane.clipping = 2;
|
||||
|
||||
backPlane.vertices = Vector.<Number>([
|
||||
-size, -size, -size,
|
||||
-size, -size, size,
|
||||
size, -size, size,
|
||||
size, -size, -size,
|
||||
]);
|
||||
frontPlane.vertices = Vector.<Number>([
|
||||
size, size, -size,
|
||||
size, size, size,
|
||||
-size, size, size,
|
||||
-size, size, -size,
|
||||
]);
|
||||
topPlane.vertices = Vector.<Number>([
|
||||
size, size, size,
|
||||
size, -size, size,
|
||||
-size, -size, size,
|
||||
-size, size, size,
|
||||
]);
|
||||
bottomPlane.vertices = Vector.<Number>([
|
||||
size, -size, -size,
|
||||
size, size, -size,
|
||||
-size, size, -size,
|
||||
-size, -size, -size,
|
||||
]);
|
||||
leftPlane.vertices = Vector.<Number>([
|
||||
-size, size, -size,
|
||||
-size, size, size,
|
||||
-size, -size, size,
|
||||
-size, -size, -size,
|
||||
]);
|
||||
rightPlane.vertices = Vector.<Number>([
|
||||
size, -size, -size,
|
||||
size, -size, size,
|
||||
size, size, size,
|
||||
size, size, -size,
|
||||
]);
|
||||
|
||||
var uvts:Vector.<Number> = Vector.<Number>([1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0]);
|
||||
backPlane.uvts = uvts;
|
||||
frontPlane.uvts = uvts;
|
||||
topPlane.uvts = uvts;
|
||||
bottomPlane.uvts = uvts;
|
||||
leftPlane.uvts = uvts;
|
||||
rightPlane.uvts = uvts;
|
||||
|
||||
var indices:Vector.<int> = Vector.<int>([4, 0, 1, 2, 3]);
|
||||
backPlane.indices = indices;
|
||||
frontPlane.indices = indices;
|
||||
topPlane.indices = indices;
|
||||
bottomPlane.indices = indices;
|
||||
leftPlane.indices = indices;
|
||||
rightPlane.indices = indices;
|
||||
|
||||
backPlane.numVertices = 4;
|
||||
frontPlane.numVertices = 4;
|
||||
topPlane.numVertices = 4;
|
||||
bottomPlane.numVertices = 4;
|
||||
leftPlane.numVertices = 4;
|
||||
rightPlane.numVertices = 4;
|
||||
|
||||
backPlane.numFaces = 1;
|
||||
frontPlane.numFaces = 1;
|
||||
topPlane.numFaces = 1;
|
||||
bottomPlane.numFaces = 1;
|
||||
leftPlane.numFaces = 1;
|
||||
rightPlane.numFaces = 1;
|
||||
|
||||
backPlane.poly = true;
|
||||
frontPlane.poly = true;
|
||||
topPlane.poly = true;
|
||||
bottomPlane.poly = true;
|
||||
leftPlane.poly = true;
|
||||
rightPlane.poly = true;
|
||||
|
||||
addChild(backPlane);
|
||||
addChild(frontPlane);
|
||||
addChild(topPlane);
|
||||
addChild(bottomPlane);
|
||||
addChild(leftPlane);
|
||||
addChild(rightPlane);
|
||||
}
|
||||
|
||||
public function set backTexture(value:BitmapData):void {
|
||||
backPlane.texture = value;
|
||||
}
|
||||
|
||||
public function set frontTexture(value:BitmapData):void {
|
||||
frontPlane.texture = value;
|
||||
}
|
||||
|
||||
public function set topTexture(value:BitmapData):void {
|
||||
topPlane.texture = value;
|
||||
}
|
||||
|
||||
public function set bottomTexture(value:BitmapData):void {
|
||||
bottomPlane.texture = value;
|
||||
}
|
||||
|
||||
public function set leftTexture(value:BitmapData):void {
|
||||
leftPlane.texture = value;
|
||||
}
|
||||
|
||||
public function set rightTexture(value:BitmapData):void {
|
||||
rightPlane.texture = value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package alternativa.engine3d.containers {
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.core.Object3DContainer;
|
||||
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class SplitContainer extends Object3DContainer {
|
||||
|
||||
public var splitPlane:Vector3D = new Vector3D(0, 0, 1, 0);
|
||||
static private const invertCameraMatrix:Matrix3D = new Matrix3D();
|
||||
static private const cameraPosition:Vector.<Number> = Vector.<Number>([0, 0, 0]);
|
||||
static private const invertCameraPosition:Vector.<Number> = new Vector.<Number>(3, true);
|
||||
|
||||
override protected function drawVisibleChildren(camera:Camera3D, object:Object3D, canvas:Canvas):void {
|
||||
var i:int;
|
||||
var child:Object3D;
|
||||
invertCameraMatrix.identity();
|
||||
invertCameraMatrix.prepend(object.cameraMatrix);
|
||||
invertCameraMatrix.invert();
|
||||
invertCameraMatrix.transformVectors(cameraPosition, invertCameraPosition);
|
||||
if (invertCameraPosition[0]*splitPlane.x + invertCameraPosition[1]*splitPlane.y + invertCameraPosition[2]*splitPlane.z < splitPlane.w) {
|
||||
for (i = 0; i < numVisibleChildren; i++) {
|
||||
child = visibleChildren[i];
|
||||
if (camera.debugMode) child.debug(camera, child, canvas);
|
||||
child.draw(camera, child, canvas);
|
||||
visibleChildren[i] = null;
|
||||
}
|
||||
} else {
|
||||
for (i = numVisibleChildren - 1; i >= 0; i--) {
|
||||
child = visibleChildren[i];
|
||||
if (camera.debugMode) child.debug(camera, child, canvas);
|
||||
child.draw(camera, child, canvas);
|
||||
visibleChildren[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
package alternativa.engine3d.controllers {
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.events.KeyboardEvent;
|
||||
import flash.events.MouseEvent;
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Vector3D;
|
||||
import flash.ui.Keyboard;
|
||||
import flash.utils.getTimer;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class SimpleObjectController {
|
||||
|
||||
/**
|
||||
* Имя действия для привязки клавиш движения вперёд.
|
||||
*/
|
||||
public static const ACTION_FORWARD:String = "ACTION_FORWARD";
|
||||
/**
|
||||
* Имя действия для привязки клавиш движения назад.
|
||||
*/
|
||||
public static const ACTION_BACK:String = "ACTION_BACK";
|
||||
/**
|
||||
* Имя действия для привязки клавиш движения влево.
|
||||
*/
|
||||
public static const ACTION_LEFT:String = "ACTION_LEFT";
|
||||
/**
|
||||
* Имя действия для привязки клавиш движения вправо.
|
||||
*/
|
||||
public static const ACTION_RIGHT:String = "ACTION_RIGHT";
|
||||
/**
|
||||
* Имя действия для привязки клавиш движения вверх.
|
||||
*/
|
||||
public static const ACTION_UP:String = "ACTION_UP";
|
||||
/**
|
||||
* Имя действия для привязки клавиш движения вниз.
|
||||
*/
|
||||
public static const ACTION_DOWN:String = "ACTION_DOWN";
|
||||
/**
|
||||
* Имя действия для привязки клавиш поворота вверх.
|
||||
*/
|
||||
public static const ACTION_PITCH_UP:String = "ACTION_PITCH_UP";
|
||||
/**
|
||||
* Имя действия для привязки клавиш поворота вниз.
|
||||
*/
|
||||
public static const ACTION_PITCH_DOWN:String = "ACTION_PITCH_DOWN";
|
||||
/**
|
||||
* Имя действия для привязки клавиш поворота налево.
|
||||
*/
|
||||
public static const ACTION_YAW_LEFT:String = "ACTION_YAW_LEFT";
|
||||
/**
|
||||
* Имя действия для привязки клавиш поворота направо.
|
||||
*/
|
||||
public static const ACTION_YAW_RIGHT:String = "ACTION_YAW_RIGHT";
|
||||
/**
|
||||
* Имя действия для привязки клавиш увеличения скорости.
|
||||
*/
|
||||
public static const ACTION_ACCELERATE:String = "ACTION_ACCELERATE";
|
||||
/**
|
||||
* Имя действия для привязки клавиш активации обзора мышью.
|
||||
*/
|
||||
public static const ACTION_MOUSE_LOOK:String = "ACTION_MOUSE_LOOK";
|
||||
|
||||
|
||||
public var speed:Number;
|
||||
public var speedMultiplier:Number;
|
||||
public var mouseSensitivity:Number;
|
||||
public var maxPitch:Number = Number.MAX_VALUE;
|
||||
public var minPitch:Number = -Number.MAX_VALUE;
|
||||
|
||||
private var eventSource:InteractiveObject;
|
||||
private var _object:Object3D;
|
||||
|
||||
private var _up:Boolean;
|
||||
private var _down:Boolean;
|
||||
private var _forward:Boolean;
|
||||
private var _back:Boolean;
|
||||
private var _left:Boolean;
|
||||
private var _right:Boolean;
|
||||
private var _accelerate:Boolean;
|
||||
|
||||
private var displacement:Vector3D = new Vector3D();
|
||||
private var mousePoint:Point = new Point();
|
||||
private var mouseLook:Boolean;
|
||||
private var objectTransform:Vector.<Vector3D>;
|
||||
|
||||
private var time:int;
|
||||
|
||||
/**
|
||||
* Ассоциативный массив, связывающий имена команд с реализующими их функциями. Функции должны иметь вид
|
||||
* function(value:Boolean):void. Значение параметра <code>value</code> указывает, нажата или отпущена соответсвующая команде
|
||||
* клавиша.
|
||||
*/
|
||||
private var actionBindings:Object = {};
|
||||
/**
|
||||
* Ассоциативный массив, связывающий коды клавиатурных клавиш с именами команд.
|
||||
*/
|
||||
protected var keyBindings:Object = {};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param eventSource источник событий для контроллера
|
||||
* @param speed скорость поступательного перемещения объекта
|
||||
* @param mouseSensitivity чувствительность мыши - количество градусов поворота на один пиксель перемещения мыши
|
||||
*/
|
||||
public function SimpleObjectController(eventSource:InteractiveObject, object:Object3D, speed:Number, speedMultiplier:Number = 3, mouseSensitivity:Number = 1) {
|
||||
this.eventSource = eventSource;
|
||||
this.object = object;
|
||||
this.speed = speed;
|
||||
this.speedMultiplier = speedMultiplier;
|
||||
this.mouseSensitivity = mouseSensitivity;
|
||||
|
||||
actionBindings[ACTION_FORWARD] = moveForward;
|
||||
actionBindings[ACTION_BACK] = moveBack;
|
||||
actionBindings[ACTION_LEFT] = moveLeft;
|
||||
actionBindings[ACTION_RIGHT] = moveRight;
|
||||
actionBindings[ACTION_UP] = moveUp;
|
||||
actionBindings[ACTION_DOWN] = moveDown;
|
||||
actionBindings[ACTION_ACCELERATE] = accelerate;
|
||||
|
||||
setDefaultBindings();
|
||||
|
||||
enable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Активирует контроллер.
|
||||
*/
|
||||
public function enable():void {
|
||||
eventSource.addEventListener(KeyboardEvent.KEY_DOWN, onKey);
|
||||
eventSource.addEventListener(KeyboardEvent.KEY_UP, onKey);
|
||||
eventSource.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
||||
eventSource.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Дективирует контроллер.
|
||||
*/
|
||||
public function disable():void {
|
||||
eventSource.removeEventListener(KeyboardEvent.KEY_DOWN, onKey);
|
||||
eventSource.removeEventListener(KeyboardEvent.KEY_UP, onKey);
|
||||
eventSource.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
||||
eventSource.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||
stopMouseLook();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function onMouseDown(e:MouseEvent):void {
|
||||
startMouseLook();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function onMouseUp(e:MouseEvent):void {
|
||||
stopMouseLook();
|
||||
}
|
||||
|
||||
/**
|
||||
* Включает режим взгляда мышью.
|
||||
*/
|
||||
public function startMouseLook():void {
|
||||
mousePoint.x = eventSource.mouseX;
|
||||
mousePoint.y = eventSource.mouseY;
|
||||
mouseLook = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Отключает режим взгляда мышью.
|
||||
*/
|
||||
public function stopMouseLook():void {
|
||||
mouseLook = false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function onKey(e:KeyboardEvent):void {
|
||||
var method:Function = keyBindings[e.keyCode];
|
||||
if (method != null) method.call(this, e.type == KeyboardEvent.KEY_DOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Управляемый объект.
|
||||
*/
|
||||
public function set object(value:Object3D):void {
|
||||
_object = value;
|
||||
updateObjectTransform();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function get object():Object3D {
|
||||
return _object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновляет инофрмацию о трансформации объекта. Метод следует вызывать после изменения матрицы объекта вне контролллера.
|
||||
*/
|
||||
public function updateObjectTransform():void {
|
||||
if (_object != null) objectTransform = _object.matrix.decompose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет новое положение объекта, используя внутренний счётчик времени.
|
||||
*/
|
||||
public function update():void {
|
||||
if (_object == null) return;
|
||||
|
||||
var frameTime:Number = time;
|
||||
time = getTimer();
|
||||
frameTime = 0.001*(time - frameTime);
|
||||
if (frameTime > 0.1) frameTime = 0.1;
|
||||
|
||||
var moved:Boolean = false;
|
||||
|
||||
if (mouseLook) {
|
||||
var dx:Number = eventSource.mouseX - mousePoint.x;
|
||||
var dy:Number = eventSource.mouseY - mousePoint.y;
|
||||
mousePoint.x = eventSource.mouseX;
|
||||
mousePoint.y = eventSource.mouseY;
|
||||
var v:Vector3D = objectTransform[1];
|
||||
v.x -= dy*Math.PI/180*mouseSensitivity;
|
||||
if (v.x > maxPitch) v.x = maxPitch;
|
||||
if (v.x < minPitch) v.x = minPitch;
|
||||
v.z -= dx*Math.PI/180*mouseSensitivity;
|
||||
moved = true;
|
||||
}
|
||||
|
||||
displacement.x = _right ? 1 : (_left ? -1 : 0);
|
||||
displacement.y = _forward ? 1 : (_back ? -1 : 0);
|
||||
displacement.z = _up ? 1 : (_down ? -1 : 0);
|
||||
if (displacement.lengthSquared > 0) {
|
||||
if (_object is Camera3D) {
|
||||
var tmp:Number = displacement.z;
|
||||
displacement.z = displacement.y;
|
||||
displacement.y = -tmp;
|
||||
}
|
||||
deltaTransformVector(displacement);
|
||||
if (_accelerate) displacement.scaleBy(speedMultiplier*speed*frameTime/displacement.length);
|
||||
else displacement.scaleBy(speed*frameTime/displacement.length);
|
||||
(objectTransform[0] as Vector3D).incrementBy(displacement);
|
||||
moved = true;
|
||||
}
|
||||
|
||||
if (moved) _object.matrix.recompose(objectTransform);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param pos
|
||||
*/
|
||||
public function setObjectPos(pos:Vector3D):void {
|
||||
if (_object != null) {
|
||||
var v:Vector3D = objectTransform[0];
|
||||
v.x = pos.x;
|
||||
v.y = pos.y;
|
||||
v.z = pos.z;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param pos
|
||||
*/
|
||||
public function setObjectPosXYZ(x:Number, y:Number, z:Number):void {
|
||||
if (_object != null) {
|
||||
var v:Vector3D = objectTransform[0];
|
||||
v.x = x;
|
||||
v.y = y;
|
||||
v.z = z;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param point
|
||||
*/
|
||||
public function lookAt(point:Vector3D):void {
|
||||
lookAtXYZ(point.x, point.y, point.z);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @param z
|
||||
*/
|
||||
public function lookAtXYZ(x:Number, y:Number, z:Number):void {
|
||||
if (_object == null) return;
|
||||
var v:Vector3D = objectTransform[0];
|
||||
var dx:Number = x - v.x;
|
||||
var dy:Number = y - v.y;
|
||||
var dz:Number = z - v.z;
|
||||
v = objectTransform[1];
|
||||
v.x = Math.atan2(dz, Math.sqrt(dx*dx + dy*dy));
|
||||
if (_object is Camera3D) v.x -= 0.5*Math.PI;
|
||||
v.y = 0;
|
||||
v.z = -Math.atan2(dx, dy);
|
||||
_object.matrix.recompose(objectTransform);
|
||||
}
|
||||
|
||||
private var _vin:Vector.<Number> = new Vector.<Number>(3);
|
||||
private var _vout:Vector.<Number> = new Vector.<Number>(3);
|
||||
|
||||
private function deltaTransformVector(v:Vector3D):void {
|
||||
_vin[0] = v.x;
|
||||
_vin[1] = v.y;
|
||||
_vin[2] = v.z;
|
||||
_object.matrix.transformVectors(_vin, _vout);
|
||||
var c:Vector3D = objectTransform[0];
|
||||
v.x = _vout[0] - c.x;
|
||||
v.y = _vout[1] - c.y;
|
||||
v.z = _vout[2] - c.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Активация движения вперёд.
|
||||
*
|
||||
* @param value <code>true</code> для начала движения, <code>false</code> для окончания
|
||||
*/
|
||||
public function moveForward(value:Boolean):void {
|
||||
_forward = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Активация движения назад.
|
||||
*
|
||||
* @param value <code>true</code> для начала движения, <code>false</code> для окончания
|
||||
*/
|
||||
public function moveBack(value:Boolean):void {
|
||||
_back = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Активация движения влево.
|
||||
*
|
||||
* @param value <code>true</code> для начала движения, <code>false</code> для окончания
|
||||
*/
|
||||
public function moveLeft(value:Boolean):void {
|
||||
_left = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Активация движения вправо.
|
||||
*
|
||||
* @param value <code>true</code> для начала движения, <code>false</code> для окончания
|
||||
*/
|
||||
public function moveRight(value:Boolean):void {
|
||||
_right = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Активация движения вверх.
|
||||
*
|
||||
* @param value <code>true</code> для начала движения, <code>false</code> для окончания
|
||||
*/
|
||||
public function moveUp(value:Boolean):void {
|
||||
_up = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Активация движения вниз.
|
||||
*
|
||||
* @param value <code>true</code> для начала движения, <code>false</code> для окончания
|
||||
*/
|
||||
public function moveDown(value:Boolean):void {
|
||||
_down = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Активация режима увеличенной скорости.
|
||||
*
|
||||
* @param value <code>true</code> для включения ускорения, <code>false</code> для выключения
|
||||
*/
|
||||
public function accelerate(value:Boolean):void {
|
||||
_accelerate = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод выполняет привязку клавиши к действию. Одной клавише может быть назначено только одно действие.
|
||||
*
|
||||
* @param keyCode код клавиши
|
||||
* @param action наименование действия
|
||||
*
|
||||
* @see #unbindKey()
|
||||
* @see #unbindAll()
|
||||
*/
|
||||
public function bindKey(keyCode:uint, action:String):void {
|
||||
var method:Function = actionBindings[action];
|
||||
if (method != null) keyBindings[keyCode] = method;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function bindKeys(bindings:Array):void {
|
||||
for (var i:int = 0; i < bindings.length; i += 2) bindKey(bindings[i], bindings[i + 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Очистка привязки клавиши.
|
||||
*
|
||||
* @param keyCode код клавиши
|
||||
*
|
||||
* @see #bindKey()
|
||||
* @see #unbindAll()
|
||||
*/
|
||||
public function unbindKey(keyCode:uint):void {
|
||||
delete keyBindings[keyCode];
|
||||
}
|
||||
|
||||
/**
|
||||
* Очистка привязки всех клавиш.
|
||||
*
|
||||
* @see #bindKey()
|
||||
* @see #unbindKey()
|
||||
*/
|
||||
public function unbindAll():void {
|
||||
for (var key:* in keyBindings) delete keyBindings[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод устанавливает привязки клавиш по умолчанию. Реализация по умолчанию не делает ничего.
|
||||
*
|
||||
* @see #bindKey()
|
||||
* @see #unbindKey()
|
||||
* @see #unbindAll()
|
||||
*/
|
||||
public function setDefaultBindings():void {
|
||||
bindKey(87, ACTION_FORWARD);
|
||||
bindKey(83, ACTION_BACK);
|
||||
bindKey(65, ACTION_LEFT);
|
||||
bindKey(68, ACTION_RIGHT);
|
||||
bindKey(69, ACTION_UP);
|
||||
bindKey(67, ACTION_DOWN);
|
||||
bindKey(Keyboard.SHIFT, ACTION_ACCELERATE);
|
||||
|
||||
bindKey(Keyboard.UP, ACTION_FORWARD);
|
||||
bindKey(Keyboard.DOWN, ACTION_BACK);
|
||||
bindKey(Keyboard.LEFT, ACTION_LEFT);
|
||||
bindKey(Keyboard.RIGHT, ACTION_RIGHT);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
public class BackfaceCulling {
|
||||
|
||||
/**
|
||||
* Отсечение по динамически расчитываемым нормалям.
|
||||
*/
|
||||
static public const DYNAMIC_NORMALS:int = 0;
|
||||
/**
|
||||
* Отсечение по предрасчитанным нормалям.
|
||||
*/
|
||||
static public const STATIC_NORMALS:int = 1;
|
||||
|
||||
}
|
||||
}
|
||||
64
Alternativa3D7/7.0/alternativa/engine3d/core/BoundBox.as
Normal file
64
Alternativa3D7/7.0/alternativa/engine3d/core/BoundBox.as
Normal file
@@ -0,0 +1,64 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
public class BoundBox {
|
||||
|
||||
public var minX:Number = Number.MAX_VALUE;
|
||||
public var minY:Number = Number.MAX_VALUE;
|
||||
public var minZ:Number = Number.MAX_VALUE;
|
||||
public var maxX:Number = -Number.MAX_VALUE;
|
||||
public var maxY:Number = -Number.MAX_VALUE;
|
||||
public var maxZ:Number = -Number.MAX_VALUE;
|
||||
|
||||
public function setSize(minX:Number, minY:Number, minZ:Number, maxX:Number, maxY:Number, maxZ:Number):void {
|
||||
this.minX = minX;
|
||||
this.minY = minY;
|
||||
this.minZ = minZ;
|
||||
this.maxX = maxX;
|
||||
this.maxY = maxY;
|
||||
this.maxZ = maxZ;
|
||||
}
|
||||
|
||||
public function addBoundBox(boundBox:BoundBox):void {
|
||||
minX = (boundBox.minX < minX) ? boundBox.minX : minX;
|
||||
minY = (boundBox.minY < minY) ? boundBox.minY : minY;
|
||||
minZ = (boundBox.minZ < minZ) ? boundBox.minZ : minZ;
|
||||
maxX = (boundBox.maxX > maxX) ? boundBox.maxX : maxX;
|
||||
maxY = (boundBox.maxY > maxY) ? boundBox.maxY : maxY;
|
||||
maxZ = (boundBox.maxZ > maxZ) ? boundBox.maxZ : maxZ;
|
||||
}
|
||||
|
||||
public function addPoint(x:Number, y:Number, z:Number):void {
|
||||
if (x < minX) minX = x;
|
||||
if (x > maxX) maxX = x;
|
||||
if (y < minY) minY = y;
|
||||
if (y > maxY) maxY = y;
|
||||
if (z < minZ) minZ = z;
|
||||
if (z > maxZ) maxZ = z;
|
||||
}
|
||||
|
||||
public function infinity():void {
|
||||
minX = minY = minZ = Number.MAX_VALUE;
|
||||
maxX = maxY = maxZ = -Number.MAX_VALUE;
|
||||
}
|
||||
|
||||
public function copyFrom(boundBox:BoundBox):void {
|
||||
minX = boundBox.minX;
|
||||
minY = boundBox.minY;
|
||||
minZ = boundBox.minZ;
|
||||
maxX = boundBox.maxX;
|
||||
maxY = boundBox.maxY;
|
||||
maxZ = boundBox.maxZ;
|
||||
}
|
||||
|
||||
public function clone():BoundBox {
|
||||
var clone:BoundBox = new BoundBox();
|
||||
clone.copyFrom(this);
|
||||
return clone;
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
return "BoundBox [" + minX.toFixed(2) + ", " + minY.toFixed(2) + ", " + minZ.toFixed(2) + " - " + maxX.toFixed(2) + ", " + maxY.toFixed(2) + ", " + maxZ.toFixed(2) + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
411
Alternativa3D7/7.0/alternativa/engine3d/core/Camera3D.as
Normal file
411
Alternativa3D7/7.0/alternativa/engine3d/core/Camera3D.as
Normal file
@@ -0,0 +1,411 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.display.Bitmap;
|
||||
import flash.display.BitmapData;
|
||||
import flash.display.Sprite;
|
||||
import flash.display.StageAlign;
|
||||
import flash.events.Event;
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Rectangle;
|
||||
import flash.geom.Vector3D;
|
||||
import flash.system.System;
|
||||
import flash.text.TextField;
|
||||
import flash.text.TextFieldAutoSize;
|
||||
import flash.text.TextFormat;
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.getDefinitionByName;
|
||||
import flash.utils.getQualifiedClassName;
|
||||
import flash.utils.getQualifiedSuperclassName;
|
||||
import flash.utils.getTimer;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class Camera3D extends Object3D {
|
||||
|
||||
/**
|
||||
* Вьюпорт камеры.
|
||||
*/
|
||||
public var canvas:Canvas;
|
||||
public var fov:Number = Math.PI/2;
|
||||
/**
|
||||
* Ширина вьюпорта.
|
||||
*/
|
||||
public var width:Number = 500;
|
||||
/**
|
||||
* Высота вьюпорта.
|
||||
*/
|
||||
public var height:Number = 500;
|
||||
|
||||
public var nearClipping:Number = 0;
|
||||
public var farClipping:Number = 5000;
|
||||
public var farFalloff:Number = 4000;
|
||||
|
||||
// Матрица проецирования
|
||||
alternativa3d var projectionMatrix:Matrix3D;
|
||||
alternativa3d var projectionMatrixData:Vector.<Number> = new Vector.<Number>(16, true);
|
||||
// Параметры перспективы
|
||||
alternativa3d var viewSize:Number;
|
||||
alternativa3d var viewSizeX:Number;
|
||||
alternativa3d var viewSizeY:Number;
|
||||
alternativa3d var perspectiveScaleX:Number;
|
||||
alternativa3d var perspectiveScaleY:Number;
|
||||
alternativa3d var focalLength:Number;
|
||||
// Перекрытия
|
||||
alternativa3d var occlusionPlanes:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>();
|
||||
alternativa3d var occlusionEdges:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>();
|
||||
alternativa3d var numOccluders:int;
|
||||
alternativa3d var occludedAll:Boolean;
|
||||
|
||||
public function Camera3D() {
|
||||
updateProjection();
|
||||
}
|
||||
|
||||
override alternativa3d function get canDraw():Boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Отрисовка иерархии объектов, в которой находится камера.
|
||||
* Перед render(), если менялись параметры камеры, нужно вызвать updateProjection().
|
||||
*/
|
||||
public function render():void {
|
||||
// Расчёт матрицы перевода из рута в камеру
|
||||
cameraMatrix.identity();
|
||||
var object:Object3D = this;
|
||||
while (object._parent != null) {
|
||||
cameraMatrix.append(object.matrix);
|
||||
object = object._parent;
|
||||
}
|
||||
cameraMatrix.invert();
|
||||
cameraMatrix.appendScale(perspectiveScaleX, perspectiveScaleY, 1);
|
||||
|
||||
numOccluders = 0;
|
||||
occludedAll = false;
|
||||
|
||||
numTriangles = 0;
|
||||
|
||||
// Отрисовка
|
||||
if (object.visible && object.canDraw) {
|
||||
object.cameraMatrix.identity();
|
||||
object.cameraMatrix.prepend(cameraMatrix);
|
||||
object.cameraMatrix.prepend(object.matrix);
|
||||
if (object.cullingInCamera(this, 63) >= 0) {
|
||||
// Отрисовка объекта
|
||||
canvas.numDraws = 0;
|
||||
if (debugMode) object.debug(this, object, canvas);
|
||||
object.draw(this, object, canvas);
|
||||
// Зачистка ненужных канвасов
|
||||
canvas.removeChildren(canvas.numDraws);
|
||||
} else {
|
||||
// Если отсеклось, зачищаем рутовый канвас
|
||||
canvas.removeChildren(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* После изменения параметров fov, width, height нужно вызвать этот метод.
|
||||
*/
|
||||
public function updateProjection():void {
|
||||
// Расчёт параметров перспективы
|
||||
viewSize = Math.sqrt(width*width + height*height)*0.5;
|
||||
focalLength = viewSize/Math.tan(fov*0.5);
|
||||
viewSizeX = width*0.5;
|
||||
viewSizeY = height*0.5;
|
||||
perspectiveScaleX = focalLength/viewSizeX;
|
||||
perspectiveScaleY = focalLength/viewSizeY;
|
||||
// Подготовка матрицы проецирования
|
||||
projectionMatrixData[0] = viewSizeX;
|
||||
projectionMatrixData[5] = viewSizeY;
|
||||
projectionMatrixData[10] = 1;
|
||||
projectionMatrixData[11] = 1;
|
||||
projectionMatrix = new Matrix3D(projectionMatrixData);
|
||||
}
|
||||
|
||||
private static var _tmpv:Vector.<Number> = new Vector.<Number>(3);
|
||||
|
||||
/**
|
||||
* @param v
|
||||
* @param result
|
||||
*/
|
||||
public function projectGlobal(v:Vector3D, result:Vector3D):void {
|
||||
_tmpv[0] = v.x; _tmpv[1] = v.y; _tmpv[2] = v.z;
|
||||
cameraMatrix.transformVectors(_tmpv, _tmpv);
|
||||
projectionMatrix.transformVectors(_tmpv, _tmpv);
|
||||
result.z = _tmpv[2];
|
||||
result.x = _tmpv[0]/result.z;
|
||||
result.y = _tmpv[1]/result.z;
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
|
||||
// Режим отладки
|
||||
public var debugMode:Boolean = false;
|
||||
|
||||
// Список объектов дебага
|
||||
private var debugSet:Object = new Object();
|
||||
|
||||
// Добавить в дебаг
|
||||
public function addToDebug(debug:int, ... rest):void {
|
||||
if (!debugSet[debug]) debugSet[debug] = new Dictionary();
|
||||
for (var i:int = 0; i < rest.length;) debugSet[debug][rest[i++]] = true;
|
||||
}
|
||||
|
||||
// Убрать из дебага
|
||||
public function removeFromDebug(debug:int, ... rest):void {
|
||||
if (debugSet[debug]) {
|
||||
for (var i:int = 0; i < rest.length;) delete debugSet[debug][rest[i++]];
|
||||
for (var key:* in debugSet[debug]);
|
||||
if (!key) delete debugSet[debug];
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка, находится ли объект или один из классов, от которых он нследован, в дебаге
|
||||
alternativa3d function checkInDebug(object:Object3D):int {
|
||||
var res:int = 0;
|
||||
for (var debug:int = 1; debug <= 256; debug = debug << 1) {
|
||||
if (debugSet[debug]) {
|
||||
if (debugSet[debug][Object3D] || debugSet[debug][object]) {
|
||||
res |= debug;
|
||||
} else {
|
||||
var objectClass:Class = getDefinitionByName(getQualifiedClassName(object)) as Class;
|
||||
while (objectClass != Object3D) {
|
||||
if (debugSet[debug][objectClass]) {
|
||||
res |= debug;
|
||||
break;
|
||||
}
|
||||
objectClass = Class(getDefinitionByName(getQualifiedSuperclassName(objectClass)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public var diagram:Sprite = createDiagram();
|
||||
|
||||
private var fpsTextField:TextField;
|
||||
private var memoryTextField:TextField;
|
||||
private var trianglesTextField:TextField;
|
||||
private var timerTextField:TextField;
|
||||
private var graph:Bitmap;
|
||||
private var rect:Rectangle;
|
||||
|
||||
private var _diagramAlign:String = "TR";
|
||||
private var _diagramHorizontalMargin:Number = 2;
|
||||
private var _diagramVerticalMargin:Number = 2;
|
||||
|
||||
public var fpsUpdatePeriod:int = 10;
|
||||
private var fpsUpdateCounter:int;
|
||||
private var previousFrameTime:int;
|
||||
private var previousPeriodTime:int;
|
||||
|
||||
private var maxMemory:int;
|
||||
alternativa3d var numTriangles:int;
|
||||
|
||||
public var timerUpdatePeriod:int = 10;
|
||||
private var timerUpdateCounter:int;
|
||||
private var timeSum:int;
|
||||
private var timeCount:int;
|
||||
private var timer:int;
|
||||
|
||||
private function createDiagram():Sprite {
|
||||
var diagram:Sprite = new Sprite();
|
||||
diagram.mouseEnabled = false;
|
||||
diagram.mouseChildren = false;
|
||||
// Инициализация диаграммы
|
||||
diagram.addEventListener(Event.ADDED_TO_STAGE, function():void {
|
||||
// FPS
|
||||
fpsTextField = new TextField();
|
||||
fpsTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0xCCCCCC);
|
||||
fpsTextField.autoSize = TextFieldAutoSize.LEFT;
|
||||
fpsTextField.text = "FPS: " + Number(diagram.stage.frameRate).toFixed(2);
|
||||
fpsTextField.selectable = false;
|
||||
fpsTextField.x = -3;
|
||||
fpsTextField.y = -5;
|
||||
diagram.addChild(fpsTextField);
|
||||
// Память
|
||||
memoryTextField = new TextField();
|
||||
memoryTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0xCCCC00);
|
||||
memoryTextField.autoSize = TextFieldAutoSize.LEFT;
|
||||
memoryTextField.text = "MEM: " + bytesToString(System.totalMemory);
|
||||
memoryTextField.selectable = false;
|
||||
memoryTextField.x = -3;
|
||||
memoryTextField.y = 4;
|
||||
diagram.addChild(memoryTextField);
|
||||
// Треугольники
|
||||
trianglesTextField = new TextField();
|
||||
trianglesTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0xFF6600);
|
||||
trianglesTextField.autoSize = TextFieldAutoSize.LEFT;
|
||||
trianglesTextField.text = "TRI: " + 0;
|
||||
trianglesTextField.selectable = false;
|
||||
trianglesTextField.x = -2;
|
||||
trianglesTextField.y = 13;
|
||||
diagram.addChild(trianglesTextField);
|
||||
// Время выполнения метода
|
||||
timerTextField = new TextField();
|
||||
timerTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0x0066FF);
|
||||
timerTextField.autoSize = TextFieldAutoSize.LEFT;
|
||||
timerTextField.text = "TMR:";
|
||||
timerTextField.selectable = false;
|
||||
timerTextField.x = -2;
|
||||
timerTextField.y = 13 + 9;
|
||||
diagram.addChild(timerTextField);
|
||||
// График
|
||||
graph = new Bitmap(new BitmapData(60, 40, true, 0x20FFFFFF));
|
||||
rect = new Rectangle(0, 0, 1, 40)
|
||||
graph.x = 0;
|
||||
graph.y = 27 + 9;
|
||||
diagram.addChild(graph);
|
||||
// Сброс параметров
|
||||
previousFrameTime = previousPeriodTime = getTimer();
|
||||
fpsUpdateCounter = 0;
|
||||
maxMemory = 0;
|
||||
timerUpdateCounter = 0;
|
||||
timeSum = 0;
|
||||
timeCount = 0;
|
||||
// Подписка
|
||||
diagram.stage.addEventListener(Event.ENTER_FRAME, updateDiagram, false, -1000);
|
||||
diagram.stage.addEventListener(Event.RESIZE, resizeDiagram, false, -1000);
|
||||
resizeDiagram();
|
||||
});
|
||||
// Деинициализация диаграммы
|
||||
diagram.addEventListener(Event.REMOVED_FROM_STAGE, function():void {
|
||||
// Обнуление
|
||||
diagram.removeChild(fpsTextField);
|
||||
diagram.removeChild(memoryTextField);
|
||||
diagram.removeChild(trianglesTextField);
|
||||
diagram.removeChild(graph);
|
||||
fpsTextField = null;
|
||||
memoryTextField = null;
|
||||
trianglesTextField = null;
|
||||
timerTextField = null;
|
||||
graph.bitmapData.dispose();
|
||||
graph = null;
|
||||
rect = null;
|
||||
// Отписка
|
||||
diagram.stage.removeEventListener(Event.ENTER_FRAME, updateDiagram);
|
||||
diagram.stage.removeEventListener(Event.RESIZE, resizeDiagram);
|
||||
});
|
||||
return diagram;
|
||||
}
|
||||
|
||||
private function resizeDiagram(e:Event = null):void {
|
||||
if (diagram.stage != null) {
|
||||
var coord:Point = diagram.parent.globalToLocal(new Point());
|
||||
if (_diagramAlign == StageAlign.TOP_LEFT || _diagramAlign == StageAlign.LEFT || _diagramAlign == StageAlign.BOTTOM_LEFT) {
|
||||
diagram.x = Math.round(coord.x + _diagramHorizontalMargin);
|
||||
}
|
||||
if (_diagramAlign == StageAlign.TOP || _diagramAlign == StageAlign.BOTTOM) {
|
||||
diagram.x = Math.round(coord.x + diagram.stage.stageWidth/2 - graph.width/2);
|
||||
}
|
||||
if (_diagramAlign == StageAlign.TOP_RIGHT || _diagramAlign == StageAlign.RIGHT || _diagramAlign == StageAlign.BOTTOM_RIGHT) {
|
||||
diagram.x = Math.round(coord.x + diagram.stage.stageWidth - _diagramHorizontalMargin - graph.width);
|
||||
}
|
||||
if (_diagramAlign == StageAlign.TOP_LEFT || _diagramAlign == StageAlign.TOP || _diagramAlign == StageAlign.TOP_RIGHT) {
|
||||
diagram.y = Math.round(coord.y + _diagramVerticalMargin);
|
||||
}
|
||||
if (_diagramAlign == StageAlign.LEFT || _diagramAlign == StageAlign.RIGHT) {
|
||||
diagram.y = Math.round(coord.y + diagram.stage.stageHeight/2 - (graph.y + graph.height)/2);
|
||||
}
|
||||
if (_diagramAlign == StageAlign.BOTTOM_LEFT || _diagramAlign == StageAlign.BOTTOM || _diagramAlign == StageAlign.BOTTOM_RIGHT) {
|
||||
diagram.y = Math.round(coord.y + diagram.stage.stageHeight - _diagramVerticalMargin - graph.y - graph.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function updateDiagram(e:Event):void {
|
||||
var fps:Number;
|
||||
var mod:int;
|
||||
var time:int = getTimer();
|
||||
var stageFrameRate:int = diagram.stage.frameRate;
|
||||
|
||||
// FPS текст
|
||||
if (++fpsUpdateCounter == fpsUpdatePeriod) {
|
||||
fps = 1000*fpsUpdatePeriod/(time - previousPeriodTime);
|
||||
if (fps > stageFrameRate) fps = stageFrameRate;
|
||||
mod = fps*100 % 100;
|
||||
fpsTextField.text = "FPS: " + int(fps) + "." + ((mod >= 10) ? mod : ((mod > 0) ? ("0" + mod) : "00"));
|
||||
previousPeriodTime = time;
|
||||
fpsUpdateCounter = 0;
|
||||
}
|
||||
// FPS график
|
||||
fps = 1000/(time - previousFrameTime);
|
||||
if (fps > stageFrameRate) fps = stageFrameRate;
|
||||
graph.bitmapData.scroll(1, 0);
|
||||
graph.bitmapData.fillRect(rect, 0x20FFFFFF);
|
||||
graph.bitmapData.setPixel32(0, 40*(1 - fps/stageFrameRate), 0xFFCCCCCC);
|
||||
previousFrameTime = time;
|
||||
|
||||
// Память текст
|
||||
var memory:int = System.totalMemory;
|
||||
memoryTextField.text = "MEM: " + bytesToString(memory);
|
||||
// Память график
|
||||
if (memory > maxMemory) maxMemory = memory;
|
||||
graph.bitmapData.setPixel32(0, 40*(1 - memory/maxMemory), 0xFFCCCC00);
|
||||
|
||||
// Треугольники текст
|
||||
trianglesTextField.text = "TRI: " + numTriangles;
|
||||
|
||||
// Время текст
|
||||
if (++timerUpdateCounter == timerUpdatePeriod) {
|
||||
if (timeCount > 0) {
|
||||
fps = timeSum/timeCount;
|
||||
mod = fps*100 % 100;
|
||||
timerTextField.text = "TMR: " + int(fps) + "." + ((mod >= 10) ? mod : ((mod > 0) ? ("0" + mod) : "00"));
|
||||
} else {
|
||||
timerTextField.text = "TMR:";
|
||||
}
|
||||
timerUpdateCounter = 0;
|
||||
timeSum = 0;
|
||||
timeCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public function startTimer():void {
|
||||
timer = getTimer();
|
||||
}
|
||||
|
||||
public function stopTimer():void {
|
||||
timeSum += getTimer() - timer;
|
||||
timeCount++;
|
||||
}
|
||||
|
||||
public function get diagramAlign():String {
|
||||
return _diagramAlign;
|
||||
}
|
||||
public function set diagramAlign(value:String):void {
|
||||
_diagramAlign = value;
|
||||
resizeDiagram();
|
||||
}
|
||||
|
||||
public function get diagramHorizontalMargin():Number {
|
||||
return _diagramHorizontalMargin;
|
||||
}
|
||||
public function set diagramHorizontalMargin(value:Number):void {
|
||||
_diagramHorizontalMargin = value;
|
||||
resizeDiagram();
|
||||
}
|
||||
|
||||
public function get diagramVerticalMargin():Number {
|
||||
return _diagramVerticalMargin;
|
||||
}
|
||||
public function set diagramVerticalMargin(value:Number):void {
|
||||
_diagramVerticalMargin = value;
|
||||
resizeDiagram();
|
||||
}
|
||||
|
||||
private function bytesToString(bytes:int):String {
|
||||
if (bytes < 1024) return bytes + "b";
|
||||
else if (bytes < 10240) return (bytes/1024).toFixed(2) + "kb";
|
||||
else if (bytes < 102400) return (bytes/1024).toFixed(1) + "kb";
|
||||
else if (bytes < 1048576) return (bytes >> 10) + "kb";
|
||||
else if (bytes < 10485760) return (bytes/1048576).toFixed(2) + "mb";
|
||||
else if (bytes < 104857600) return (bytes/1048576).toFixed(1) + "mb";
|
||||
else return (bytes >> 20) + "mb";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
101
Alternativa3D7/7.0/alternativa/engine3d/core/Canvas.as
Normal file
101
Alternativa3D7/7.0/alternativa/engine3d/core/Canvas.as
Normal file
@@ -0,0 +1,101 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.display.DisplayObject;
|
||||
import flash.display.Graphics;
|
||||
import flash.display.Sprite;
|
||||
import flash.geom.ColorTransform;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class Canvas extends Sprite {
|
||||
|
||||
static private const defaultColorTransform:ColorTransform = new ColorTransform();
|
||||
static private const collector:Vector.<Canvas> = new Vector.<Canvas>();
|
||||
static private var collectorLength:int = 0;
|
||||
|
||||
alternativa3d var gfx:Graphics = graphics;
|
||||
|
||||
private var modifiedGraphics:Boolean;
|
||||
private var modifiedAlpha:Boolean;
|
||||
private var modifiedBlendMode:Boolean;
|
||||
private var modifiedColorTransform:Boolean;
|
||||
private var modifiedFilters:Boolean;
|
||||
|
||||
alternativa3d var _numChildren:int = 0;
|
||||
alternativa3d var numDraws:int = 0;
|
||||
|
||||
public function Canvas() {
|
||||
mouseEnabled = false;
|
||||
mouseChildren = false;
|
||||
}
|
||||
|
||||
alternativa3d function getChildCanvas(useGraphics:Boolean, useChildren:Boolean, alpha:Number = 1, blendMode:String = "normal", colorTransform:ColorTransform = null, filters:Array = null):Canvas {
|
||||
var canvas:Canvas;
|
||||
var displayObject:DisplayObject;
|
||||
// Зачистка не канвасов
|
||||
while (_numChildren > numDraws && !((displayObject = getChildAt(_numChildren - 1 - numDraws)) is Canvas)) {
|
||||
removeChild(displayObject);
|
||||
_numChildren--;
|
||||
}
|
||||
// Получение канваса
|
||||
if (_numChildren > numDraws++) {
|
||||
canvas = displayObject as Canvas;
|
||||
// Зачистка
|
||||
canvas.gfx.clear();
|
||||
if (canvas._numChildren > 0 && !useChildren) {
|
||||
canvas.removeChildren(0);
|
||||
}
|
||||
} else {
|
||||
canvas = (collectorLength > 0) ? collector[--collectorLength] : new Canvas();
|
||||
addChildAt(canvas, 0);
|
||||
_numChildren++;
|
||||
}
|
||||
// Пометка о том, что в graphics будет что-то нарисовано
|
||||
canvas.modifiedGraphics = useGraphics;
|
||||
// Установка свойств
|
||||
if (alpha != 1) {
|
||||
canvas.alpha = alpha;
|
||||
canvas.modifiedAlpha = true;
|
||||
} else if (canvas.modifiedAlpha) {
|
||||
canvas.alpha = 1;
|
||||
canvas.modifiedAlpha = false;
|
||||
}
|
||||
if (blendMode != "normal") {
|
||||
canvas.blendMode = blendMode;
|
||||
canvas.modifiedBlendMode = true;
|
||||
} else if (canvas.modifiedBlendMode) {
|
||||
canvas.blendMode = "normal";
|
||||
canvas.modifiedBlendMode = false;
|
||||
}
|
||||
if (colorTransform != null) {
|
||||
colorTransform.alphaMultiplier = alpha;
|
||||
canvas.transform.colorTransform = colorTransform;
|
||||
canvas.modifiedColorTransform = true;
|
||||
} else if (canvas.modifiedColorTransform) {
|
||||
defaultColorTransform.alphaMultiplier = alpha;
|
||||
canvas.transform.colorTransform = defaultColorTransform;
|
||||
canvas.modifiedColorTransform = false;
|
||||
}
|
||||
if (filters != null) {
|
||||
canvas.filters = filters;
|
||||
canvas.modifiedFilters = true;
|
||||
} else if (canvas.modifiedFilters) {
|
||||
canvas.filters = null;
|
||||
canvas.modifiedFilters = false;
|
||||
}
|
||||
return canvas;
|
||||
}
|
||||
|
||||
alternativa3d function removeChildren(keep:int):void {
|
||||
for (var canvas:Canvas; _numChildren > keep; _numChildren--) {
|
||||
if ((canvas = removeChildAt(0) as Canvas) != null) {
|
||||
if (canvas.modifiedGraphics) canvas.gfx.clear();
|
||||
if (canvas._numChildren > 0) canvas.removeChildren(0);
|
||||
collector[collectorLength++] = canvas;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
19
Alternativa3D7/7.0/alternativa/engine3d/core/Clipping.as
Normal file
19
Alternativa3D7/7.0/alternativa/engine3d/core/Clipping.as
Normal file
@@ -0,0 +1,19 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
public class Clipping {
|
||||
|
||||
/**
|
||||
* Объект отсекается целиком, если он полностью вне пирамиды видимости или пересекает nearClipping камеры.
|
||||
*/
|
||||
static public const BOUND_CULLING:int = 0;
|
||||
/**
|
||||
* Грань отсекается целиком, если она полностью вне пирамиды видимости или пересекает nearClipping камеры.
|
||||
*/
|
||||
static public const FACE_CULLING:int = 1;
|
||||
/**
|
||||
* Грань подрезается пирамидой видимости камеры.
|
||||
*/
|
||||
static public const FACE_CLIPPING:int = 2;
|
||||
|
||||
}
|
||||
}
|
||||
18
Alternativa3D7/7.0/alternativa/engine3d/core/Debug.as
Normal file
18
Alternativa3D7/7.0/alternativa/engine3d/core/Debug.as
Normal file
@@ -0,0 +1,18 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
public class Debug {
|
||||
|
||||
static public const NAMES:int = 1;
|
||||
static public const AXES:int = 2;
|
||||
static public const CENTERS:int = 4;
|
||||
static public const BOUNDS:int = 8;
|
||||
|
||||
static public const EDGES:int = 16;
|
||||
static public const VERTICES:int = 32;
|
||||
static public const NORMALS:int = 64;
|
||||
|
||||
static public const NODES:int = 128;
|
||||
static public const SPLITS:int = 256;
|
||||
|
||||
}
|
||||
}
|
||||
47
Alternativa3D7/7.0/alternativa/engine3d/core/Fragment.as
Normal file
47
Alternativa3D7/7.0/alternativa/engine3d/core/Fragment.as
Normal file
@@ -0,0 +1,47 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
public class Fragment {
|
||||
|
||||
static public var collector:Fragment;
|
||||
|
||||
public var next:Fragment;
|
||||
|
||||
public var negative:Fragment;
|
||||
public var positive:Fragment;
|
||||
|
||||
public var geometry:Geometry;
|
||||
|
||||
public var indices:Vector.<int> = new Vector.<int>();
|
||||
public var num:int = 0;
|
||||
|
||||
public var normalX:Number;
|
||||
public var normalY:Number;
|
||||
public var normalZ:Number;
|
||||
public var offset:Number;
|
||||
|
||||
static public function create():Fragment {
|
||||
if (collector != null) {
|
||||
var res:Fragment = collector;
|
||||
collector = collector.next;
|
||||
res.next = null;
|
||||
res.num = 0;
|
||||
return res;
|
||||
} else {
|
||||
return new Fragment();
|
||||
}
|
||||
}
|
||||
|
||||
public function create():Fragment {
|
||||
if (collector != null) {
|
||||
var res:Fragment = collector;
|
||||
collector = collector.next;
|
||||
res.next = null;
|
||||
res.num = 0;
|
||||
return res;
|
||||
} else {
|
||||
return new Fragment();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
1616
Alternativa3D7/7.0/alternativa/engine3d/core/Geometry.as
Normal file
1616
Alternativa3D7/7.0/alternativa/engine3d/core/Geometry.as
Normal file
File diff suppressed because it is too large
Load Diff
70
Alternativa3D7/7.0/alternativa/engine3d/core/MipMap.as
Normal file
70
Alternativa3D7/7.0/alternativa/engine3d/core/MipMap.as
Normal file
@@ -0,0 +1,70 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.display.BitmapData;
|
||||
import flash.filters.ConvolutionFilter;
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Rectangle;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Объект, представляющий текстуру в виде последовательности её уменьшенных копий.
|
||||
* Каждая следующая в два раза меньше предыдущей. Последняя имеет размер 1х1 пиксел.
|
||||
* Чем дальше от камеры отрисовываемый объект, тем меньшая текстура выбирается.
|
||||
* Это позволяет получить лучший визуальный результат и большую производительность.
|
||||
*/
|
||||
public class MipMap {
|
||||
|
||||
/**
|
||||
* Мип-текстуры
|
||||
*/
|
||||
public var textures:Vector.<BitmapData> = new Vector.<BitmapData>();
|
||||
/**
|
||||
* Количество мип-текстур
|
||||
*/
|
||||
public var num:int;
|
||||
/**
|
||||
* Отношение размера пиксела текстуры к единице измерения трёхмерного пространства
|
||||
*/
|
||||
public var resolution:Number;
|
||||
|
||||
static private const filter:ConvolutionFilter = new ConvolutionFilter(2, 2, [1, 1, 1, 1], 4, 0, false, true);
|
||||
static private const point:Point = new Point();
|
||||
static private const matrix:Matrix = new Matrix();
|
||||
static private const rect:Rectangle = new Rectangle();
|
||||
public function MipMap(texture:BitmapData, resolution:Number = 1) {
|
||||
var bmp:BitmapData = new BitmapData(texture.width, texture.height, texture.transparent);
|
||||
var current:BitmapData = textures[num++] = texture;
|
||||
filter.preserveAlpha = !texture.transparent;
|
||||
var w:Number = rect.width = texture.width, h:Number = rect.height = texture.height;
|
||||
while (w > 1 && h > 1 && rect.width > 1 && rect.height > 1) {
|
||||
bmp.applyFilter(current, rect, point, filter);
|
||||
rect.width = w >> 1;
|
||||
rect.height = h >> 1;
|
||||
matrix.a = rect.width/w;
|
||||
matrix.d = rect.height/h;
|
||||
w *= 0.5;
|
||||
h *= 0.5;
|
||||
current = new BitmapData(rect.width, rect.height, texture.transparent, 0);
|
||||
current.draw(bmp, matrix, null, null, null, false);
|
||||
textures[num++] = current;
|
||||
}
|
||||
bmp.dispose();
|
||||
this.resolution = resolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение мип-уровня в зависимости от удалённости объекта от камеры
|
||||
* @param distance Z-координата объекта в пространстве камеры
|
||||
* @param camera Камера
|
||||
* @return Индекс в списке мип-текстур textures
|
||||
*/
|
||||
public function getLevel(distance:Number, camera:Camera3D):int {
|
||||
var level:int = Math.log(distance/(camera.focalLength*resolution))*1.442695040888963387;
|
||||
if (level < 0) return 0; else if (level >= num) return num - 1;
|
||||
return level;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Alternativa3D7/7.0/alternativa/engine3d/core/MipMapping.as
Normal file
18
Alternativa3D7/7.0/alternativa/engine3d/core/MipMapping.as
Normal file
@@ -0,0 +1,18 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
public class MipMapping {
|
||||
|
||||
/**
|
||||
* Нет мипмаппинга.
|
||||
*/
|
||||
static public const NONE:int = 0;
|
||||
/**
|
||||
* Мипмаппинг по удалённости объекта от камеры.
|
||||
*/
|
||||
static public const PER_OBJECT:int = 1;
|
||||
|
||||
//static public const PER_POLYGON:int = 2;
|
||||
//static public const PER_PIXEL:int = 3;
|
||||
|
||||
}
|
||||
}
|
||||
276
Alternativa3D7/7.0/alternativa/engine3d/core/Object3D.as
Normal file
276
Alternativa3D7/7.0/alternativa/engine3d/core/Object3D.as
Normal file
@@ -0,0 +1,276 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.geom.ColorTransform;
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Utils3D;
|
||||
import flash.utils.getQualifiedClassName;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Базовый трёхмерный объект
|
||||
*/
|
||||
public class Object3D {
|
||||
|
||||
public var name:String;
|
||||
/**
|
||||
* Матрица трансформации. Управлять трансформацией объекта можно только через это свойство
|
||||
* путём назначения новой матрицы или с помощью методов матрицы.
|
||||
*/
|
||||
public var matrix:Matrix3D = new Matrix3D();
|
||||
public var visible:Boolean = true;
|
||||
|
||||
public var alpha:Number = 1;
|
||||
public var blendMode:String = "normal";
|
||||
public var colorTransform:ColorTransform = null;
|
||||
public var filters:Array = null;
|
||||
|
||||
alternativa3d var _parent:Object3DContainer;
|
||||
|
||||
alternativa3d var _boundBox:BoundBox;
|
||||
|
||||
alternativa3d var culling:int = 0;
|
||||
|
||||
alternativa3d var cameraMatrix:Matrix3D = new Matrix3D();
|
||||
|
||||
static private const boundBoxVertices:Vector.<Number> = new Vector.<Number>(24, true);
|
||||
|
||||
public function removeFromParent():void {
|
||||
if (_parent != null)
|
||||
_parent.removeChild(this);
|
||||
}
|
||||
|
||||
alternativa3d function get canDraw():Boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {}
|
||||
|
||||
alternativa3d function debug(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
var debugResult:int = camera.checkInDebug(this);
|
||||
if (debugResult == 0) return;
|
||||
var canvas:Canvas = parentCanvas.getChildCanvas(true, false);
|
||||
if (debugResult & Debug.AXES) object.drawAxes(camera, canvas);
|
||||
if (debugResult & Debug.CENTERS) object.drawCenter(camera, canvas);
|
||||
if (debugResult & Debug.NAMES) object.drawName(camera, canvas);
|
||||
if (debugResult & Debug.BOUNDS) object.drawBoundBox(camera, canvas);
|
||||
}
|
||||
|
||||
alternativa3d function getGeometry(camera:Camera3D, object:Object3D):Geometry {
|
||||
return null;
|
||||
}
|
||||
|
||||
alternativa3d function split(normalX:Number, normalY:Number, normalZ:Number, offset:Number, threshold:Number):Vector.<Object3D> {
|
||||
return new Vector.<Object3D>(2);
|
||||
}
|
||||
|
||||
public function get boundBox():BoundBox {
|
||||
return _boundBox;
|
||||
}
|
||||
|
||||
public function set boundBox(value:BoundBox):void {
|
||||
_boundBox = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Расчёт баунда
|
||||
* @param matrix Трансформация пространства, в системе которого расчитывается баунд.
|
||||
* Если этот параметр не указан, баунд расчитается в локальных координатах объекта.
|
||||
* @param boundBox Баунд, в который записывается результат.
|
||||
* Если этот параметр не указан, создаётся новый экземпляр.
|
||||
* @return Расчитанный баунд.
|
||||
*/
|
||||
public function calculateBoundBox(matrix:Matrix3D = null, boundBox:BoundBox = null):BoundBox {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function get parent():Object3DContainer {
|
||||
return _parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function cullingInCamera(camera:Camera3D, parentCulling:int):int {
|
||||
if (camera.occludedAll) return -1;
|
||||
culling = parentCulling;
|
||||
var i:int, infront:Boolean, behind:Boolean, boundBox:BoundBox = this.boundBox, numOccluders:int = camera.numOccluders, cull:Boolean = culling > 0 && boundBox != null, occlude:Boolean = numOccluders > 0 && boundBox != null;
|
||||
// Расчёт точек баунда в координатах камеры
|
||||
if (cull || occlude) boundBoxVertices[0] = boundBoxVertices[3] = boundBoxVertices[6] = boundBoxVertices[9] = boundBox.minX, boundBoxVertices[1] = boundBoxVertices[4] = boundBoxVertices[13] = boundBoxVertices[16] = boundBox.minY, boundBoxVertices[2] = boundBoxVertices[8] = boundBoxVertices[14] = boundBoxVertices[20] = boundBox.minZ, boundBoxVertices[12] = boundBoxVertices[15] = boundBoxVertices[18] = boundBoxVertices[21] = boundBox.maxX, boundBoxVertices[7] = boundBoxVertices[10] = boundBoxVertices[19] = boundBoxVertices[22] = boundBox.maxY, boundBoxVertices[5] = boundBoxVertices[11] = boundBoxVertices[17] = boundBoxVertices[23] = boundBox.maxZ, cameraMatrix.transformVectors(boundBoxVertices, boundBoxVertices);
|
||||
// Куллинг
|
||||
if (cull) {
|
||||
if (culling & 1) {
|
||||
for (i = 2, infront = false, behind = false; i <= 23; i += 3) {
|
||||
if (boundBoxVertices[i] > camera.nearClipping) {
|
||||
infront = true;
|
||||
if (behind) break;
|
||||
} else {
|
||||
behind = true;
|
||||
if (infront) break;
|
||||
}
|
||||
}
|
||||
if (behind) {
|
||||
if (!infront) return -1;
|
||||
} else {
|
||||
// TODO: проверка не нужна
|
||||
if (infront) culling &= 62;
|
||||
}
|
||||
}
|
||||
if (culling & 2) {
|
||||
for (i = 2, infront = false, behind = false; i <= 23; i += 3) {
|
||||
if (boundBoxVertices[i] < camera.farClipping) {
|
||||
infront = true;
|
||||
if (behind) break;
|
||||
} else {
|
||||
behind = true;
|
||||
if (infront) break;
|
||||
}
|
||||
}
|
||||
if (behind) {
|
||||
if (!infront) return -1;
|
||||
} else {
|
||||
if (infront) culling &= 61;
|
||||
}
|
||||
}
|
||||
if (culling & 4) {
|
||||
for (i = 0, infront = false, behind = false; i <= 21; i += 3) {
|
||||
if (-boundBoxVertices[i] < boundBoxVertices[int(i + 2)]) {
|
||||
infront = true;
|
||||
if (behind) break;
|
||||
} else {
|
||||
behind = true;
|
||||
if (infront) break;
|
||||
}
|
||||
}
|
||||
if (behind) {
|
||||
if (!infront) return -1;
|
||||
} else {
|
||||
if (infront) culling &= 59;
|
||||
}
|
||||
}
|
||||
if (culling & 8) {
|
||||
for (i = 0, infront = false, behind = false; i <= 21; i += 3) {
|
||||
if (boundBoxVertices[i] < boundBoxVertices[int(i + 2)]) {
|
||||
infront = true;
|
||||
if (behind) break;
|
||||
} else {
|
||||
behind = true;
|
||||
if (infront) break;
|
||||
}
|
||||
}
|
||||
if (behind) {
|
||||
if (!infront) return -1;
|
||||
} else {
|
||||
if (infront) culling &= 55;
|
||||
}
|
||||
}
|
||||
if (culling & 16) {
|
||||
for (i = 1, infront = false, behind = false; i <= 22; i += 3) {
|
||||
if (-boundBoxVertices[i] < boundBoxVertices[int(i + 1)]) {
|
||||
infront = true;
|
||||
if (behind) break;
|
||||
} else {
|
||||
behind = true;
|
||||
if (infront) break;
|
||||
}
|
||||
}
|
||||
if (behind) {
|
||||
if (!infront) return -1;
|
||||
} else {
|
||||
if (infront) culling &= 47;
|
||||
}
|
||||
}
|
||||
if (culling & 32) {
|
||||
for (i = 1, infront = false, behind = false; i <= 22; i += 3) {
|
||||
if (boundBoxVertices[i] < boundBoxVertices[int(i + 1)]) {
|
||||
infront = true;
|
||||
if (behind) break;
|
||||
} else {
|
||||
behind = true;
|
||||
if (infront) break;
|
||||
}
|
||||
}
|
||||
if (behind) {
|
||||
if (!infront) return -1;
|
||||
} else {
|
||||
if (infront) culling &= 31;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Окклюдинг
|
||||
if (occlude) {
|
||||
for (var o:int = 0; o < numOccluders; o++) {
|
||||
var planeOccluder:Vector.<Number> = camera.occlusionPlanes[o], planeOccluderLength:int = planeOccluder.length;
|
||||
for (var ni:int = 0; ni < planeOccluderLength; ni += 3) {
|
||||
var nx:Number = planeOccluder[ni], ny:Number = planeOccluder[int(ni + 1)], nz:Number = planeOccluder[int(ni + 2)];
|
||||
for (i = 0; i < 24; i += 3) if (nx*boundBoxVertices[i] + ny*boundBoxVertices[int(i + 1)] + nz*boundBoxVertices[int(i + 2)] >= 0) break;
|
||||
if (i < 24) break;
|
||||
}
|
||||
if (ni == planeOccluderLength) return -1;
|
||||
}
|
||||
}
|
||||
return culling;
|
||||
}
|
||||
|
||||
alternativa3d function drawAxes(camera:Camera3D, canvas:Canvas):void {
|
||||
|
||||
}
|
||||
|
||||
alternativa3d function drawCenter(camera:Camera3D, canvas:Canvas):void {
|
||||
|
||||
}
|
||||
|
||||
alternativa3d function drawName(camera:Camera3D, canvas:Canvas):void {
|
||||
|
||||
}
|
||||
|
||||
static private const boundBoxProjectedVertices:Vector.<Number> = new Vector.<Number>(16, true);
|
||||
static private const boundBoxUVTs:Vector.<Number> = new Vector.<Number>(24, true);
|
||||
alternativa3d function drawBoundBox(camera:Camera3D, canvas:Canvas, color:int = -1):void {
|
||||
|
||||
var boundBox:BoundBox = this.boundBox;
|
||||
if (boundBox == null) return;
|
||||
|
||||
boundBoxVertices[0] = boundBoxVertices[3] = boundBoxVertices[6] = boundBoxVertices[9] = boundBox.minX;
|
||||
boundBoxVertices[1] = boundBoxVertices[4] = boundBoxVertices[13] = boundBoxVertices[16] = boundBox.minY;
|
||||
boundBoxVertices[2] = boundBoxVertices[8] = boundBoxVertices[14] = boundBoxVertices[20] = boundBox.minZ;
|
||||
|
||||
boundBoxVertices[12] = boundBoxVertices[15] = boundBoxVertices[18] = boundBoxVertices[21] = boundBox.maxX;
|
||||
boundBoxVertices[7] = boundBoxVertices[10] = boundBoxVertices[19] = boundBoxVertices[22] = boundBox.maxY;
|
||||
boundBoxVertices[5] = boundBoxVertices[11] = boundBoxVertices[17] = boundBoxVertices[23] = boundBox.maxZ;
|
||||
|
||||
cameraMatrix.transformVectors(boundBoxVertices, boundBoxVertices);
|
||||
for (var i:int = 0; i < 8; i++) {
|
||||
if (boundBoxVertices[int(i*3 +2)] <= 0) return;
|
||||
}
|
||||
Utils3D.projectVectors(camera.projectionMatrix, boundBoxVertices, boundBoxProjectedVertices, boundBoxUVTs);
|
||||
canvas.gfx.endFill();
|
||||
canvas.gfx.lineStyle(0, (color < 0) ? ((culling > 0) ? 0xFFFF00 : 0x00FF00) : color);
|
||||
canvas.gfx.moveTo(boundBoxProjectedVertices[0], boundBoxProjectedVertices[1]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[2], boundBoxProjectedVertices[3]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[6], boundBoxProjectedVertices[7]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[4], boundBoxProjectedVertices[5]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[0], boundBoxProjectedVertices[1]);
|
||||
canvas.gfx.moveTo(boundBoxProjectedVertices[8], boundBoxProjectedVertices[9]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[10], boundBoxProjectedVertices[11]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[14], boundBoxProjectedVertices[15]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[12], boundBoxProjectedVertices[13]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[8], boundBoxProjectedVertices[9]);
|
||||
canvas.gfx.moveTo(boundBoxProjectedVertices[0], boundBoxProjectedVertices[1]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[8], boundBoxProjectedVertices[9]);
|
||||
canvas.gfx.moveTo(boundBoxProjectedVertices[2], boundBoxProjectedVertices[3]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[10], boundBoxProjectedVertices[11]);
|
||||
canvas.gfx.moveTo(boundBoxProjectedVertices[4], boundBoxProjectedVertices[5]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[12], boundBoxProjectedVertices[13]);
|
||||
canvas.gfx.moveTo(boundBoxProjectedVertices[6], boundBoxProjectedVertices[7]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[14], boundBoxProjectedVertices[15]);
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
var className:String = getQualifiedClassName(this);
|
||||
return "[" + className.substr(className.indexOf("::") + 2) + " " + name + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.geom.ColorTransform;
|
||||
import flash.geom.Matrix3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Базовый контейнер трёхмерных объектов.
|
||||
* Логика контейнеров и child-parent-отношений идентична логике
|
||||
* displayObject'ов во Flash.
|
||||
*/
|
||||
public class Object3DContainer extends Object3D {
|
||||
|
||||
protected var children:Vector.<Object3D> = new Vector.<Object3D>();
|
||||
protected var _numChildren:int = 0;
|
||||
|
||||
protected var visibleChildren:Vector.<Object3D> = new Vector.<Object3D>();
|
||||
protected var numVisibleChildren:int = 0;
|
||||
|
||||
override alternativa3d function get canDraw():Boolean {
|
||||
return _numChildren > 0;
|
||||
}
|
||||
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
// Сбор видимых объектов
|
||||
numVisibleChildren = 0;
|
||||
for (var i:int = 0; i < _numChildren; i++) {
|
||||
var child:Object3D = children[i];
|
||||
if (child.visible && child.canDraw) {
|
||||
child.cameraMatrix.identity();
|
||||
child.cameraMatrix.prepend(object.cameraMatrix);
|
||||
child.cameraMatrix.prepend(child.matrix);
|
||||
if (child.cullingInCamera(camera, object.culling) >= 0) {
|
||||
visibleChildren[numVisibleChildren] = child;
|
||||
numVisibleChildren++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Если есть видимые объекты
|
||||
if (numVisibleChildren > 0) {
|
||||
// Подготовка канваса
|
||||
var canvas:Canvas = parentCanvas.getChildCanvas(false, true, object.alpha, object.blendMode, object.colorTransform, object.filters);
|
||||
canvas.numDraws = 0;
|
||||
// Отрисовка видимых объектов
|
||||
drawVisibleChildren(camera, object, canvas);
|
||||
// Если была отрисовка
|
||||
if (canvas.numDraws > 0) {
|
||||
canvas.removeChildren(canvas.numDraws);
|
||||
} else {
|
||||
parentCanvas.numDraws--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function drawVisibleChildren(camera:Camera3D, object:Object3D, canvas:Canvas):void {
|
||||
for (var i:int = numVisibleChildren - 1; i >= 0; i--) {
|
||||
var child:Object3D = visibleChildren[i];
|
||||
if (camera.debugMode) child.debug(camera, child, canvas);
|
||||
child.draw(camera, child, canvas);
|
||||
visibleChildren[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
override alternativa3d function getGeometry(camera:Camera3D, object:Object3D):Geometry {
|
||||
var i:int;
|
||||
var first:Geometry;
|
||||
var last:Geometry;
|
||||
var geometry:Geometry;
|
||||
for (i = 0; i < _numChildren; i++) {
|
||||
var child:Object3D = children[i];
|
||||
if (child.visible && child.canDraw) {
|
||||
child.cameraMatrix.identity();
|
||||
child.cameraMatrix.prepend(object.cameraMatrix);
|
||||
child.cameraMatrix.prepend(child.matrix);
|
||||
if (child.cullingInCamera(camera, object.culling) >= 0) {
|
||||
geometry = child.getGeometry(camera, child);
|
||||
if (geometry != null) {
|
||||
if (first != null) {
|
||||
last.next = geometry;
|
||||
} else {
|
||||
first = geometry;
|
||||
last = geometry;
|
||||
}
|
||||
while (last.next != null) {
|
||||
last = last.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (object.alpha != 1) {
|
||||
geometry = first;
|
||||
while (geometry != null) {
|
||||
geometry.alpha *= object.alpha;
|
||||
geometry = geometry.next;
|
||||
}
|
||||
}
|
||||
if (object.blendMode != "normal") {
|
||||
geometry = first;
|
||||
while (geometry != null) {
|
||||
if (geometry.blendMode == "normal") {
|
||||
geometry.blendMode = object.blendMode;
|
||||
}
|
||||
geometry = geometry.next;
|
||||
}
|
||||
}
|
||||
if (object.colorTransform != null) {
|
||||
geometry = first;
|
||||
while (geometry != null) {
|
||||
if (geometry.colorTransform != null) {
|
||||
var ct:ColorTransform = new ColorTransform(object.colorTransform.redMultiplier, object.colorTransform.greenMultiplier, object.colorTransform.blueMultiplier, object.colorTransform.alphaMultiplier, object.colorTransform.redOffset, object.colorTransform.greenOffset, object.colorTransform.blueOffset, object.colorTransform.alphaOffset);
|
||||
ct.concat(geometry.colorTransform);
|
||||
geometry.colorTransform = ct;
|
||||
} else {
|
||||
geometry.colorTransform = object.colorTransform;
|
||||
}
|
||||
geometry = geometry.next;
|
||||
}
|
||||
}
|
||||
if (object.filters != null) {
|
||||
geometry = first;
|
||||
while (geometry != null) {
|
||||
if (geometry.filters != null) {
|
||||
var fs:Array = new Array();
|
||||
var fsLength:int = 0;
|
||||
var num:int = geometry.filters.length;
|
||||
for (i = 0; i < num; i++) {
|
||||
fs[fsLength] = geometry.filters[i]; fsLength++;
|
||||
}
|
||||
num = object.filters.length;
|
||||
for (i = 0; i < num; i++) {
|
||||
fs[fsLength] = object.filters[i]; fsLength++;
|
||||
}
|
||||
geometry.filters = fs;
|
||||
} else {
|
||||
geometry.filters = object.filters;
|
||||
}
|
||||
geometry = geometry.next;
|
||||
}
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
override public function calculateBoundBox(matrix:Matrix3D = null, boundBox:BoundBox = null):BoundBox {
|
||||
var m:Matrix3D = matrix != null ? matrix.clone() : new Matrix3D();
|
||||
// Если указан баунд-бокс
|
||||
if (boundBox != null) {
|
||||
boundBox.infinity();
|
||||
} else {
|
||||
boundBox = new BoundBox();
|
||||
}
|
||||
// Расчитываем баунды объектов
|
||||
var childBoundBox:BoundBox = new BoundBox();
|
||||
var childMatrix:Matrix3D = new Matrix3D();
|
||||
for (var i:int = 0; i < _numChildren; i++) {
|
||||
var child:Object3D = children[i];
|
||||
childMatrix.identity();
|
||||
childMatrix.prepend(m);
|
||||
childMatrix.prepend(child.matrix);
|
||||
child.calculateBoundBox(childMatrix, childBoundBox);
|
||||
boundBox.minX = (childBoundBox.minX < boundBox.minX) ? childBoundBox.minX : boundBox.minX;
|
||||
boundBox.minY = (childBoundBox.minY < boundBox.minY) ? childBoundBox.minY : boundBox.minY;
|
||||
boundBox.minZ = (childBoundBox.minZ < boundBox.minZ) ? childBoundBox.minZ : boundBox.minZ;
|
||||
boundBox.maxX = (childBoundBox.maxX > boundBox.maxX) ? childBoundBox.maxX : boundBox.maxX;
|
||||
boundBox.maxY = (childBoundBox.maxY > boundBox.maxY) ? childBoundBox.maxY : boundBox.maxY;
|
||||
boundBox.maxZ = (childBoundBox.maxZ > boundBox.maxZ) ? childBoundBox.maxZ : boundBox.maxZ;
|
||||
}
|
||||
return boundBox;
|
||||
}
|
||||
|
||||
public function addChild(child:Object3D):void {
|
||||
children[_numChildren++] = child;
|
||||
child._parent = this;
|
||||
}
|
||||
|
||||
public function removeChild(child:Object3D):void {
|
||||
var i:int = children.indexOf(child);
|
||||
if (i < 0) throw new ArgumentError("Child not found");
|
||||
_numChildren--;
|
||||
for (; i < _numChildren; i++) children[i] = children[int(i + 1)];
|
||||
children.length = _numChildren;
|
||||
child._parent = null;
|
||||
}
|
||||
|
||||
public function hasChild(child:Object3D):Boolean {
|
||||
return children.indexOf(child) > -1;
|
||||
}
|
||||
|
||||
public function getChildAt(index:uint):Object3D {
|
||||
return children[index];
|
||||
}
|
||||
|
||||
public function get numChildren():uint {
|
||||
return _numChildren;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
23
Alternativa3D7/7.0/alternativa/engine3d/core/Sorting.as
Normal file
23
Alternativa3D7/7.0/alternativa/engine3d/core/Sorting.as
Normal file
@@ -0,0 +1,23 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
public class Sorting {
|
||||
|
||||
/**
|
||||
* Грани не сортируются.
|
||||
*/
|
||||
static public const NONE:int = 0;
|
||||
/**
|
||||
* Грани сортируются по средним Z.
|
||||
*/
|
||||
static public const AVERAGE_Z:int = 1;
|
||||
/**
|
||||
* Грани при отрисовке образуют BSP-дерево.
|
||||
*/
|
||||
static public const DYNAMIC_BSP:int = 2;
|
||||
/**
|
||||
* Грани находятся в BSP-дереве.
|
||||
*/
|
||||
static public const STATIC_BSP:int = 3;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
import alternativa.engine3d.loaders.events.BatchTextureLoaderErrorEvent;
|
||||
import alternativa.engine3d.loaders.events.LoaderEvent;
|
||||
import alternativa.engine3d.loaders.events.LoaderProgressEvent;
|
||||
|
||||
import flash.display.BitmapData;
|
||||
import flash.events.ErrorEvent;
|
||||
import flash.events.Event;
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.system.LoaderContext;
|
||||
|
||||
/**
|
||||
* Событие посылается, когда начинается загрузка пакета.
|
||||
*
|
||||
* @eventType flash.events.Event.OPEN
|
||||
*/
|
||||
[Event (name="open", type="flash.events.Event")]
|
||||
/**
|
||||
* Событие посылается, когда загрузка пакета успешно завершена.
|
||||
*
|
||||
* @eventType flash.events.Event.COMPLETE
|
||||
*/
|
||||
[Event (name="complete", type="flash.events.Event")]
|
||||
/**
|
||||
* Событие посылается при возникновении ошибки загрузки.
|
||||
*
|
||||
* @eventType alternativa.engine3d.loaders.events.BatchTextureLoaderErrorEvent.LOADER_ERROR
|
||||
*/
|
||||
[Event (name="loaderError", type="alternativa.engine3d.loaders.events.BatchTextureLoaderErrorEvent")]
|
||||
/**
|
||||
* Событие посылается, когда начинается загрузка очередной текстуры.
|
||||
*
|
||||
* @eventType alternativa.engine3d.loaders.events.LoaderEvent.PART_OPEN
|
||||
*/
|
||||
[Event (name="partOpen", type="alternativa.engine3d.loaders.events.LoaderEvent")]
|
||||
/**
|
||||
* Событие посылается, когда загрузка очередной текстуры успешно завершена.
|
||||
*
|
||||
* @eventType alternativa.engine3d.loaders.events.LoaderEvent.PART_COMPLETE
|
||||
*/
|
||||
[Event (name="partComplete", type="alternativa.engine3d.loaders.events.LoaderEvent")]
|
||||
/**
|
||||
* Событие посылается для отображения прогресса загрузки.
|
||||
*
|
||||
* @eventType alternativa.engine3d.loaders.events.LoaderProgressEvent.LOADER_PROGRESS
|
||||
*/
|
||||
[Event (name="loaderProgress", type="alternativa.engine3d.loaders.events.LoaderProgressEvent")]
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Пакетный загрузчик текстур.
|
||||
*
|
||||
* При возникновении ошибки во время загрузки очередной текстуры, пакетный загрузчик заменяет соответствующую текстуру изображением-заглушкой и
|
||||
* генерирует событие ошибки пакетного загрузчика. Пользователь пакетного загрузчика в обработчике ошибки может решить, прерывать ли процесс
|
||||
* загрузки вызовом метода close() или нет.
|
||||
*/
|
||||
public class BatchTextureLoader extends EventDispatcher {
|
||||
/**
|
||||
* Текстура-заглушка для замены незагруженных текстур.
|
||||
*/
|
||||
private static var stubBitmapData:BitmapData;
|
||||
|
||||
private static const IDLE:int = 0;
|
||||
private static const LOADING:int = 1;
|
||||
|
||||
// Состояние загрузчика
|
||||
private var state:int = IDLE;
|
||||
|
||||
// Загрузчик текстур
|
||||
private var textureLoader:TextureLoader;
|
||||
// Контекст безопасности загрузчика
|
||||
private var loaderContext:LoaderContext;
|
||||
// Базовый URL файлов текстур
|
||||
private var baseURL:String;
|
||||
// Пакет с описанием текстур материалов (textureName => TextureInfo)
|
||||
private var batch:Object;
|
||||
// Список имён текстур в пакете
|
||||
private var textureNames:Vector.<String>;
|
||||
// Индекс текущего материала.
|
||||
private var textureIndex:int;
|
||||
// Общее количество загружаемых текстур
|
||||
private var numTextures:int;
|
||||
// Результирующая таблица (textureName => BitmapData)
|
||||
private var _textures:Object;
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр загрузчика.
|
||||
*/
|
||||
public function BatchTextureLoader() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Результирующая таблица битмапов. Ключами являются имена текстур, значениями -- объекты класса BitmapData.
|
||||
*/
|
||||
public function get textures():Object {
|
||||
return _textures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Прекращает текущую загрузку.
|
||||
*/
|
||||
public function close():void {
|
||||
if (state == LOADING) {
|
||||
textureLoader.close();
|
||||
cleanup();
|
||||
_textures = null;
|
||||
state = IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает ссылку на загруженный список текстур материалов.
|
||||
*/
|
||||
public function unload():void {
|
||||
_textures = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Запускает загрузку.
|
||||
*
|
||||
* @param baseURL базовый URL файлов текстур
|
||||
* @param batch описание пакета текстур -- таблица textureName => TextureInfo
|
||||
* @param loaderContext LoaderContext для загрузки
|
||||
*/
|
||||
public function load(baseURL:String, batch:Object, loaderContext:LoaderContext = null):void {
|
||||
if (baseURL == null) {
|
||||
throw ArgumentError("Parameter baseURL cannot be null");
|
||||
}
|
||||
if (batch == null) {
|
||||
throw ArgumentError("Parameter batch cannot be null");
|
||||
}
|
||||
|
||||
this.baseURL = baseURL;
|
||||
this.batch = batch;
|
||||
this.loaderContext = loaderContext;
|
||||
|
||||
if (textureLoader == null) {
|
||||
textureLoader = new TextureLoader();
|
||||
} else {
|
||||
close();
|
||||
}
|
||||
textureLoader.addEventListener(Event.OPEN, onTextureLoadingStart);
|
||||
textureLoader.addEventListener(LoaderProgressEvent.LOADER_PROGRESS, onProgress);
|
||||
textureLoader.addEventListener(Event.COMPLETE, onTextureLoadingComplete);
|
||||
textureLoader.addEventListener(IOErrorEvent.IO_ERROR, onLoadingError);
|
||||
|
||||
// Получение массива имён текстур
|
||||
textureNames = new Vector.<String>();
|
||||
for (var textureName:String in batch) {
|
||||
textureNames.push(textureName);
|
||||
}
|
||||
numTextures = textureNames.length;
|
||||
// Старт загрузки
|
||||
textureIndex = 0;
|
||||
_textures = {};
|
||||
|
||||
if (hasEventListener(Event.OPEN)) {
|
||||
dispatchEvent(new Event(Event.OPEN));
|
||||
}
|
||||
|
||||
state = LOADING;
|
||||
loadNextTexture();
|
||||
}
|
||||
|
||||
/**
|
||||
* Запускает загрузку очередной текстуры.
|
||||
*/
|
||||
private function loadNextTexture():void {
|
||||
var info:TextureInfo = batch[textureNames[textureIndex]];
|
||||
var opacityMapFileUrl:String = info.opacityMapFileName == null || info.opacityMapFileName == "" ? null : baseURL + info.opacityMapFileName;
|
||||
textureLoader.load(baseURL + info.diffuseMapFileName, opacityMapFileUrl, loaderContext);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function onTextureLoadingStart(e:Event):void {
|
||||
if (hasEventListener(LoaderEvent.PART_OPEN)) {
|
||||
dispatchEvent(new LoaderEvent(LoaderEvent.PART_OPEN, numTextures, textureIndex));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function onProgress(e:LoaderProgressEvent):void {
|
||||
if (hasEventListener(LoaderProgressEvent.LOADER_PROGRESS)) {
|
||||
var totalProgress:Number = (textureIndex + e.totalProgress)/numTextures;
|
||||
dispatchEvent(new LoaderProgressEvent(LoaderProgressEvent.LOADER_PROGRESS, numTextures, textureIndex, totalProgress, e.bytesLoaded, e.bytesTotal));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Обрабатывает завершение загрузки текстуры.
|
||||
*/
|
||||
private function onTextureLoadingComplete(e:Event):void {
|
||||
_textures[textureNames[textureIndex]] = textureLoader.bitmapData;
|
||||
tryNextTexure();
|
||||
}
|
||||
|
||||
/**
|
||||
* Обрабатывает ошибку при загрузке текстуры. Незагруженная текстура заменяется изображением-заглушкой и
|
||||
* генерируется событие ошибки пакетного загрузчика.
|
||||
*/
|
||||
private function onLoadingError(e:ErrorEvent):void {
|
||||
var textureName:String = textureNames[textureIndex];
|
||||
_textures[textureName] = getStubBitmapData();
|
||||
dispatchEvent(new BatchTextureLoaderErrorEvent(BatchTextureLoaderErrorEvent.LOADER_ERROR, textureName, e.text));
|
||||
tryNextTexure();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function tryNextTexure():void {
|
||||
// Проверка состояния необходима, т.к. оно могло измениться в результате вызова метода close() в обработчике события ошибки загрузки
|
||||
if (state == IDLE) return;
|
||||
|
||||
if (hasEventListener(LoaderEvent.PART_COMPLETE)) {
|
||||
dispatchEvent(new LoaderEvent(LoaderEvent.PART_COMPLETE, numTextures, textureIndex));
|
||||
}
|
||||
if (++textureIndex == numTextures) {
|
||||
// Загружены все текстуры, отправляется сообщение о завершении
|
||||
cleanup();
|
||||
removeEventListeners();
|
||||
state = IDLE;
|
||||
if (hasEventListener(Event.COMPLETE)) {
|
||||
dispatchEvent(new Event(Event.COMPLETE));
|
||||
}
|
||||
} else {
|
||||
loadNextTexture();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function removeEventListeners():void {
|
||||
textureLoader.removeEventListener(Event.OPEN, onTextureLoadingStart);
|
||||
textureLoader.removeEventListener(LoaderProgressEvent.LOADER_PROGRESS, onProgress);
|
||||
textureLoader.removeEventListener(Event.COMPLETE, onTextureLoadingComplete);
|
||||
textureLoader.removeEventListener(IOErrorEvent.IO_ERROR, onLoadingError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает внутренние ссылки на объекты.
|
||||
*/
|
||||
private function cleanup():void {
|
||||
loaderContext = null;
|
||||
textureNames = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод для получения текстуры-заглушки.
|
||||
*
|
||||
* @return текстура-заглушка для замещения незагруженных текстур
|
||||
*/
|
||||
private function getStubBitmapData():BitmapData {
|
||||
if (stubBitmapData == null) {
|
||||
var size:uint = 20;
|
||||
stubBitmapData = new BitmapData(size, size, false, 0);
|
||||
for (var i:uint = 0; i < size; i++) {
|
||||
for (var j:uint = 0; j < size; j += 2) {
|
||||
stubBitmapData.setPixel((i%2) ? j : (j + 1), i, 0xFF00FF);
|
||||
}
|
||||
}
|
||||
}
|
||||
return stubBitmapData;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
924
Alternativa3D7/7.0/alternativa/engine3d/loaders/Loader3DS.as
Normal file
924
Alternativa3D7/7.0/alternativa/engine3d/loaders/Loader3DS.as
Normal file
@@ -0,0 +1,924 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
import flash.display.BitmapData;
|
||||
import flash.display.BlendMode;
|
||||
import flash.events.Event;
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.events.SecurityErrorEvent;
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Orientation3D;
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Vector3D;
|
||||
import flash.net.URLLoader;
|
||||
import flash.net.URLLoaderDataFormat;
|
||||
import flash.net.URLRequest;
|
||||
import flash.system.LoaderContext;
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Endian;
|
||||
|
||||
[Event (name="complete", type="flash.events.Event")]
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Loader3DS extends EventDispatcher {
|
||||
|
||||
private static const STATE_IDLE:int = -1;
|
||||
private static const STATE_LOADING_MODEL:int = 0;
|
||||
private static const STATE_LOADING_TEXTURES:int = 1;
|
||||
|
||||
private static var stubBitmapData:BitmapData;
|
||||
|
||||
private var _content:Vector.<Mesh>;
|
||||
private var version:uint;
|
||||
private var objectDatas:Object;
|
||||
private var animationDatas:Array;
|
||||
private var materialDatas:Array;
|
||||
private var bitmaps:Array;
|
||||
|
||||
private var modelLoader:URLLoader;
|
||||
private var textureLoader:TextureLoader;
|
||||
private var data:ByteArray;
|
||||
|
||||
private var counter:int;
|
||||
private var textureMaterialNames:Array;
|
||||
private var context:LoaderContext;
|
||||
private var path:String;
|
||||
|
||||
private var loaderState:int = STATE_IDLE;
|
||||
|
||||
/**
|
||||
* Повтор текстуры при заливке для создаваемых текстурных материалов.
|
||||
*
|
||||
* @see alternativa.engine3d.materials.TextureMaterial
|
||||
*/
|
||||
public var repeat:Boolean = true;
|
||||
/**
|
||||
* Сглаживание текстур при увеличении масштаба.
|
||||
*
|
||||
* @see alternativa.engine3d.materials.TextureMaterial
|
||||
*/
|
||||
public var smooth:Boolean = false;
|
||||
/**
|
||||
* Режим наложения цвета для создаваемых текстурных материалов.
|
||||
*
|
||||
* @see alternativa.engine3d.materials.TextureMaterial
|
||||
*/
|
||||
public var blendMode:String = BlendMode.NORMAL;
|
||||
|
||||
/**
|
||||
* Коэффициент пересчёта единиц измерения модели.
|
||||
*/
|
||||
public var units:Number = 1;
|
||||
/**
|
||||
* Устанавливаемый уровень мобильности загруженных объектов.
|
||||
*/
|
||||
public var mobility:int = 0;
|
||||
|
||||
/**
|
||||
* Прекращение текущей загрузки.
|
||||
*/
|
||||
public function close():void {
|
||||
if (loaderState == STATE_LOADING_MODEL) {
|
||||
modelLoader.close();
|
||||
}
|
||||
if (loaderState == STATE_LOADING_TEXTURES) {
|
||||
textureLoader.close();
|
||||
}
|
||||
loaderState = STATE_IDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод очищает внутренние ссылки на загруженные данные чтобы сборщик мусора мог освободить занимаемую ими память. Метод не работает
|
||||
* во время загрузки.
|
||||
*/
|
||||
public function unload():void {
|
||||
if (loaderState == STATE_IDLE) {
|
||||
clean();
|
||||
}
|
||||
}
|
||||
|
||||
private function clean():void {
|
||||
_content = null;
|
||||
objectDatas = null;
|
||||
animationDatas = null;
|
||||
materialDatas = null;
|
||||
bitmaps = null;
|
||||
textureMaterialNames = null;
|
||||
}
|
||||
|
||||
public function load(url:String, context:LoaderContext = null):void {
|
||||
path = url.substring(0, url.lastIndexOf("/") + 1);
|
||||
this.context = context;
|
||||
|
||||
// Очистка
|
||||
version = 0;
|
||||
clean();
|
||||
|
||||
if (modelLoader == null) {
|
||||
modelLoader = new URLLoader();
|
||||
modelLoader.dataFormat = URLLoaderDataFormat.BINARY;
|
||||
modelLoader.addEventListener(Event.COMPLETE, on3DSLoad);
|
||||
modelLoader.addEventListener(IOErrorEvent.IO_ERROR, on3DSError);
|
||||
modelLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, on3DSError);
|
||||
} else {
|
||||
close();
|
||||
}
|
||||
|
||||
loaderState = STATE_LOADING_MODEL;
|
||||
modelLoader.load(new URLRequest(url));
|
||||
}
|
||||
|
||||
private function on3DSLoad(e:Event):void {
|
||||
loaderState = STATE_IDLE;
|
||||
data = modelLoader.data;
|
||||
data.endian = Endian.LITTLE_ENDIAN;
|
||||
parse3DSChunk(0, data.bytesAvailable);
|
||||
}
|
||||
|
||||
private function on3DSError(e:Event):void {
|
||||
loaderState = STATE_IDLE;
|
||||
dispatchEvent(e);
|
||||
}
|
||||
|
||||
private function loadBitmaps():void {
|
||||
if (textureLoader == null) {
|
||||
textureLoader = new TextureLoader();
|
||||
textureLoader.addEventListener(Event.COMPLETE, loadNextBitmap);
|
||||
textureLoader.addEventListener(IOErrorEvent.IO_ERROR, loadNextBitmap);
|
||||
}
|
||||
|
||||
// Имена материалов с диффузными текстурами собираются в массив textureMaterialNames
|
||||
bitmaps = new Array();
|
||||
textureMaterialNames = new Array();
|
||||
for each (var materialData:MaterialData in materialDatas) {
|
||||
if (materialData.diffuseMap != null) {
|
||||
textureMaterialNames.push(materialData.name);
|
||||
}
|
||||
}
|
||||
|
||||
loaderState = STATE_LOADING_TEXTURES;
|
||||
loadNextBitmap();
|
||||
}
|
||||
|
||||
private function loadNextBitmap(e:Event = null):void {
|
||||
if (e != null) {
|
||||
if (!(e is IOErrorEvent)) {
|
||||
bitmaps[textureMaterialNames[counter]] = textureLoader.bitmapData;
|
||||
} else {
|
||||
if (stubBitmapData == null) {
|
||||
var size:uint = 20;
|
||||
stubBitmapData = new BitmapData(size, size, false, 0);
|
||||
for (var i:uint = 0; i < size; i++) {
|
||||
for (var j:uint = 0; j < size; j+=2) {
|
||||
stubBitmapData.setPixel((i % 2) ? j : (j+1), i, 0xFF00FF);
|
||||
}
|
||||
}
|
||||
}
|
||||
bitmaps[textureMaterialNames[counter]] = stubBitmapData;
|
||||
}
|
||||
} else {
|
||||
counter = -1;
|
||||
}
|
||||
counter++;
|
||||
if (counter < textureMaterialNames.length) {
|
||||
var materialData:MaterialData = materialDatas[textureMaterialNames[counter]];
|
||||
textureLoader.load(path + materialData.diffuseMap.filename, materialData.opacityMap == null ? null : path + materialData.opacityMap.filename, context);
|
||||
} else {
|
||||
loaderState = STATE_IDLE;
|
||||
buildContent();
|
||||
}
|
||||
}
|
||||
|
||||
private function buildContent():void {
|
||||
var i:uint;
|
||||
var length:uint;
|
||||
|
||||
// Формируем связи объектов
|
||||
_content = new Vector.<Mesh>();
|
||||
|
||||
// Создаём материалы
|
||||
var materialData:MaterialData;
|
||||
for (var materialName:String in materialDatas) {
|
||||
materialData = materialDatas[materialName];
|
||||
var mapData:MapData = materialData.diffuseMap;
|
||||
var materialMatrix:Matrix = new Matrix();
|
||||
if (mapData != null) {
|
||||
var rot:Number = mapData.rotation*Math.PI/180;
|
||||
var rotSin:Number = Math.sin(rot);
|
||||
var rotCos:Number = Math.cos(rot);
|
||||
materialMatrix.translate(-mapData.offsetU, mapData.offsetV);
|
||||
materialMatrix.translate(-0.5, -0.5);
|
||||
materialMatrix.rotate(-rot);
|
||||
materialMatrix.scale(mapData.scaleU, mapData.scaleV);
|
||||
materialMatrix.translate(0.5, 0.5);
|
||||
}
|
||||
materialData.matrix = materialMatrix;
|
||||
}
|
||||
|
||||
// Если есть данные об анимации и иерархии объектов
|
||||
var objectName:String;
|
||||
var objectData:ObjectData;
|
||||
var mesh:Mesh;
|
||||
if (animationDatas != null) {
|
||||
if (objectDatas != null) {
|
||||
|
||||
length = animationDatas.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var animationData:AnimationData = animationDatas[i];
|
||||
objectName = animationData.objectName;
|
||||
objectData = objectDatas[objectName];
|
||||
|
||||
// Если на один объект приходится несколько данных об анимации
|
||||
if (objectData != null) {
|
||||
var nameCounter:uint = 2;
|
||||
for (var j:uint = i + 1; j < length; j++) {
|
||||
var animationData2:AnimationData = animationDatas[j];
|
||||
if (objectName == animationData2.objectName) {
|
||||
var newName:String = objectName + nameCounter;
|
||||
var newObjectData:ObjectData = new ObjectData();
|
||||
animationData2.objectName = newName;
|
||||
newObjectData.name = newName;
|
||||
if (objectData.vertices != null) {
|
||||
newObjectData.vertices = new Vector.<Vector3D>().concat(objectData.vertices);
|
||||
}
|
||||
if (objectData.uvs != null) {
|
||||
newObjectData.uvs = new new Vector.<Point>().concat(objectData.uvs);
|
||||
}
|
||||
if (objectData.matrix != null) {
|
||||
newObjectData.matrix = objectData.matrix.clone();
|
||||
}
|
||||
if (objectData.faces != null) {
|
||||
newObjectData.faces = new Array().concat(objectData.faces);
|
||||
}
|
||||
if (objectData.surfaces != null) {
|
||||
newObjectData.surfaces = objectData.surfaces.clone();
|
||||
}
|
||||
objectDatas[newName] = newObjectData;
|
||||
nameCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (objectData != null && objectData.vertices != null) {
|
||||
// Меш
|
||||
mesh = new Mesh();
|
||||
mesh.createEmptyGeometry(objectData.vertices.length, objectData.faces.length);
|
||||
animationData.object = mesh;
|
||||
buildObject(animationData);
|
||||
buildMesh(mesh, objectData, animationData);
|
||||
_content.push(mesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (objectName in objectDatas) {
|
||||
objectData = objectDatas[objectName];
|
||||
if (objectData.vertices != null) {
|
||||
// Меш
|
||||
mesh = new Mesh();
|
||||
mesh.createEmptyGeometry(objectData.vertices.length, objectData.faces.length);
|
||||
buildMesh(mesh, objectData, null);
|
||||
_content.push(mesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Рассылаем событие о завершении
|
||||
dispatchEvent(new Event(Event.COMPLETE));
|
||||
}
|
||||
|
||||
private function buildObject(animationData:AnimationData):void {
|
||||
var object:Mesh = animationData.object;
|
||||
object.name = animationData.objectName;
|
||||
var transform:Vector.<Vector3D> = new Vector.<Vector3D>(3, true);
|
||||
transform[0] = (animationData.position == null) ? new Vector3D() : new Vector3D(animationData.position.x * units, animationData.position.y * units, animationData.position.z * units);
|
||||
transform[1] = (animationData.rotation == null) ? new Vector3D() : animationData.rotation.clone();
|
||||
transform[2] = (animationData.scale == null) ? new Vector3D(1, 1, 1) : animationData.scale.clone();
|
||||
object.matrix.recompose(transform, Orientation3D.AXIS_ANGLE);
|
||||
}
|
||||
|
||||
private function buildMesh(mesh:Mesh, objectData:ObjectData, animationData:AnimationData):void {
|
||||
// Добавляем вершины
|
||||
var i:uint;
|
||||
var j:uint;
|
||||
var key:*;
|
||||
var length:uint = objectData.vertices.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var vertexData:Vector3D = objectData.vertices[i];
|
||||
var uv:Point = objectData.uvs[i];
|
||||
j = i*3;
|
||||
mesh.vertices[j] = vertexData.x;
|
||||
mesh.vertices[j + 1] = vertexData.y;
|
||||
mesh.vertices[j + 2] = vertexData.z;
|
||||
mesh.uvts[j] = uv.x;
|
||||
mesh.uvts[j + 1] = 1 - uv.y;
|
||||
}
|
||||
|
||||
// Коррекция вершин
|
||||
if (animationData != null) {
|
||||
// Инвертируем матрицу
|
||||
objectData.matrix.invert();
|
||||
|
||||
// Вычитаем точку привязки из смещения матрицы
|
||||
if (animationData.pivot != null) {
|
||||
objectData.matrix.appendTranslation(-animationData.pivot.x, -animationData.pivot.y, -animationData.pivot.z);
|
||||
}
|
||||
|
||||
// Трансформируем вершины
|
||||
objectData.matrix.transformVectors(mesh.vertices, mesh.vertices);
|
||||
}
|
||||
for (i = 0; i < mesh.numVertices*3; i++) {
|
||||
mesh.vertices[i] *= units;
|
||||
}
|
||||
|
||||
// Добавляем грани
|
||||
length = objectData.faces.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var faceData:FaceData = objectData.faces[i];
|
||||
j = i*3;
|
||||
mesh.indices[j] = faceData.a;
|
||||
mesh.indices[j + 1] = faceData.b;
|
||||
mesh.indices[j + 2] = faceData.c;
|
||||
}
|
||||
|
||||
// Добавляем поверхности
|
||||
if (objectData.surfaces != null) {
|
||||
for (var surfaceId:String in objectData.surfaces) {
|
||||
var materialData:MaterialData = materialDatas[surfaceId];
|
||||
if (materialData.diffuseMap != null) {
|
||||
mesh.texture = bitmaps[materialData.name];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private function parse3DSChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Главный
|
||||
case 0x4D4D:
|
||||
parseMainChunk(dataIndex, dataLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parse3DSChunk(index + chunkLength, length - chunkLength);
|
||||
} else {
|
||||
// Загрузка битмап
|
||||
loadBitmaps();
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMainChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Версия
|
||||
case 0x0002:
|
||||
parseVersion(dataIndex);
|
||||
break;
|
||||
// 3D-сцена
|
||||
case 0x3D3D:
|
||||
parse3DChunk(dataIndex, dataLength);
|
||||
break;
|
||||
// Анимация
|
||||
case 0xB000:
|
||||
parseAnimationChunk(dataIndex, dataLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parseMainChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseVersion(index:uint):void {
|
||||
data.position = index;
|
||||
version = data.readUnsignedInt();
|
||||
}
|
||||
|
||||
private function parse3DChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Материал
|
||||
case 0xAFFF:
|
||||
// Парсим материал
|
||||
var material:MaterialData = new MaterialData();
|
||||
parseMaterialChunk(material, dataIndex, dataLength);
|
||||
break;
|
||||
// Объект
|
||||
case 0x4000:
|
||||
// Создаём данные объекта
|
||||
var object:ObjectData = new ObjectData();
|
||||
var objectLength:uint = parseObject(object, dataIndex);
|
||||
// Парсим объект
|
||||
parseObjectChunk(object, dataIndex + objectLength, dataLength - objectLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parse3DChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMaterialChunk(material:MaterialData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Имя материала
|
||||
case 0xA000:
|
||||
parseMaterialName(material, dataIndex);
|
||||
break;
|
||||
// Ambient color
|
||||
case 0xA010:
|
||||
break;
|
||||
// Diffuse color
|
||||
case 0xA020:
|
||||
data.position = dataIndex + 6;
|
||||
material.color = (data.readUnsignedByte() << 16) + (data.readUnsignedByte() << 8) + data.readUnsignedByte();
|
||||
break;
|
||||
// Specular color
|
||||
case 0xA030:
|
||||
break;
|
||||
// Shininess percent
|
||||
case 0xA040:
|
||||
data.position = dataIndex + 6;
|
||||
material.glossiness = data.readUnsignedShort();
|
||||
break;
|
||||
// Shininess strength percent
|
||||
case 0xA041:
|
||||
data.position = dataIndex + 6;
|
||||
material.specular = data.readUnsignedShort();
|
||||
break;
|
||||
// Transperensy
|
||||
case 0xA050:
|
||||
data.position = dataIndex + 6;
|
||||
material.transparency = data.readUnsignedShort();
|
||||
break;
|
||||
// Texture map 1
|
||||
case 0xA200:
|
||||
material.diffuseMap = new MapData();
|
||||
parseMapChunk(material.name, material.diffuseMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Texture map 2
|
||||
case 0xA33A:
|
||||
break;
|
||||
// Opacity map
|
||||
case 0xA210:
|
||||
material.opacityMap = new MapData();
|
||||
parseMapChunk(material.name, material.opacityMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Bump map
|
||||
case 0xA230:
|
||||
//material.normalMap = new MapData();
|
||||
//parseMapChunk(material.normalMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Shininess map
|
||||
case 0xA33C:
|
||||
break;
|
||||
// Specular map
|
||||
case 0xA204:
|
||||
break;
|
||||
// Self-illumination map
|
||||
case 0xA33D:
|
||||
break;
|
||||
// Reflection map
|
||||
case 0xA220:
|
||||
break;
|
||||
}
|
||||
|
||||
parseMaterialChunk(material, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMaterialName(material:MaterialData, index:uint):void {
|
||||
// Создаём список материалов, если надо
|
||||
if (materialDatas == null) {
|
||||
materialDatas = new Array();
|
||||
}
|
||||
// Получаем название материала
|
||||
material.name = getString(index);
|
||||
// Помещаем данные материала в список
|
||||
materialDatas[material.name] = material;
|
||||
}
|
||||
|
||||
private function parseMapChunk(materialName:String, map:MapData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Имя файла
|
||||
case 0xA300:
|
||||
map.filename = getString(dataIndex).toLowerCase();
|
||||
break;
|
||||
// Масштаб по U
|
||||
case 0xA354:
|
||||
data.position = dataIndex;
|
||||
map.scaleU = data.readFloat();
|
||||
break;
|
||||
// Масштаб по V
|
||||
case 0xA356:
|
||||
data.position = dataIndex;
|
||||
map.scaleV = data.readFloat();
|
||||
break;
|
||||
// Смещение по U
|
||||
case 0xA358:
|
||||
data.position = dataIndex;
|
||||
map.offsetU = data.readFloat();
|
||||
break;
|
||||
// Смещение по V
|
||||
case 0xA35A:
|
||||
data.position = dataIndex;
|
||||
map.offsetV = data.readFloat();
|
||||
break;
|
||||
// Угол поворота
|
||||
case 0xA35C:
|
||||
data.position = dataIndex;
|
||||
map.rotation = data.readFloat();
|
||||
break;
|
||||
}
|
||||
|
||||
parseMapChunk(materialName, map, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseObject(object:ObjectData, index:uint):uint {
|
||||
// Создаём список объектов, если надо
|
||||
if (objectDatas == null) {
|
||||
objectDatas = new Object();
|
||||
}
|
||||
// Получаем название объекта
|
||||
object.name = getString(index);
|
||||
// Помещаем данные объекта в список
|
||||
objectDatas[object.name] = object;
|
||||
return object.name.length + 1;
|
||||
}
|
||||
|
||||
private function parseObjectChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Меш
|
||||
case 0x4100:
|
||||
parseMeshChunk(object, dataIndex, dataLength);
|
||||
break;
|
||||
// Источник света
|
||||
case 0x4600:
|
||||
break;
|
||||
// Камера
|
||||
case 0x4700:
|
||||
break;
|
||||
}
|
||||
|
||||
parseObjectChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMeshChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Вершины
|
||||
case 0x4110:
|
||||
parseVertices(object, dataIndex);
|
||||
break;
|
||||
// UV
|
||||
case 0x4140:
|
||||
parseUVs(object, dataIndex);
|
||||
break;
|
||||
// Трансформация
|
||||
case 0x4160:
|
||||
parseMatrix(object, dataIndex);
|
||||
break;
|
||||
// Грани
|
||||
case 0x4120:
|
||||
var facesLength:uint = parseFaces(object, dataIndex);
|
||||
parseFacesChunk(object, dataIndex + facesLength, dataLength - facesLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parseMeshChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseVertices(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.vertices = new Vector.<Vector3D>();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
object.vertices.push(new Vector3D(data.readFloat(), data.readFloat(), data.readFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
private function parseUVs(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.uvs = new Vector.<Point>();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
object.uvs.push(new Point(data.readFloat(), data.readFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMatrix(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
object.matrix = new Matrix3D(Vector.<Number>([
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 0,
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 0,
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 0,
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 1
|
||||
]));
|
||||
}
|
||||
|
||||
private function parseFaces(object:ObjectData, index:uint):uint {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.faces = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
var face:FaceData = new FaceData();
|
||||
face.a = data.readUnsignedShort();
|
||||
face.b = data.readUnsignedShort();
|
||||
face.c = data.readUnsignedShort();
|
||||
object.faces.push(face);
|
||||
data.position += 2; // Пропускаем флаг
|
||||
}
|
||||
return 2 + num*8;
|
||||
}
|
||||
|
||||
private function parseFacesChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Поверхности
|
||||
case 0x4130:
|
||||
parseSurface(object, dataIndex);
|
||||
break;
|
||||
// Группы сглаживания
|
||||
case 0x4150:
|
||||
break;
|
||||
}
|
||||
|
||||
parseFacesChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseSurface(object:ObjectData, index:uint):void {
|
||||
// Создаём данные поверхности
|
||||
var surface:SurfaceData = new SurfaceData();
|
||||
// Создаём список поверхностей, если надо
|
||||
if (object.surfaces == null) {
|
||||
object.surfaces = new Object();
|
||||
}
|
||||
// Получаем название материала
|
||||
surface.materialName = getString(index);
|
||||
// Помещаем данные поверхности в список
|
||||
object.surfaces[surface.materialName] = surface;
|
||||
|
||||
// Получаем грани поверхности
|
||||
data.position = index + surface.materialName.length + 1;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
surface.faces = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
surface.faces.push(data.readUnsignedShort());
|
||||
}
|
||||
}
|
||||
|
||||
private function parseAnimationChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Анимация объекта
|
||||
case 0xB001:
|
||||
case 0xB002:
|
||||
case 0xB003:
|
||||
case 0xB004:
|
||||
case 0xB005:
|
||||
case 0xB006:
|
||||
case 0xB007:
|
||||
var animation:AnimationData = new AnimationData();
|
||||
if (animationDatas == null) {
|
||||
animationDatas = new Array();
|
||||
}
|
||||
animationDatas.push(animation);
|
||||
parseObjectAnimationChunk(animation, dataIndex, dataLength);
|
||||
break;
|
||||
|
||||
// Таймлайн
|
||||
case 0xB008:
|
||||
break;
|
||||
}
|
||||
|
||||
parseAnimationChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseObjectAnimationChunk(animation:AnimationData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Идентификация объекта и его связь
|
||||
case 0xB010:
|
||||
parseObjectAnimationInfo(animation, dataIndex);
|
||||
break;
|
||||
// Точка привязки объекта (pivot)
|
||||
case 0xB013:
|
||||
parseObjectAnimationPivot(animation, dataIndex);
|
||||
break;
|
||||
// Смещение объекта относительно родителя
|
||||
case 0xB020:
|
||||
parseObjectAnimationPosition(animation, dataIndex);
|
||||
break;
|
||||
// Поворот объекта относительно родителя (angle-axis)
|
||||
case 0xB021:
|
||||
parseObjectAnimationRotation(animation, dataIndex);
|
||||
break;
|
||||
// Масштабирование объекта относительно родителя
|
||||
case 0xB022:
|
||||
parseObjectAnimationScale(animation, dataIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
parseObjectAnimationChunk(animation, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseObjectAnimationInfo(animation:AnimationData, index:uint):void {
|
||||
var name:String = getString(index);
|
||||
data.position = index + name.length + 1 + 4;
|
||||
animation.objectName = name;
|
||||
animation.parentIndex = data.readUnsignedShort();
|
||||
}
|
||||
|
||||
private function parseObjectAnimationPivot(animation:AnimationData, index:uint):void {
|
||||
data.position = index;
|
||||
animation.pivot = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
private function parseObjectAnimationPosition(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
animation.position = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
private function parseObjectAnimationRotation(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
var angle:Number = data.readFloat();
|
||||
animation.rotation = new Vector3D(-data.readFloat(), -data.readFloat(), -data.readFloat(), angle);
|
||||
}
|
||||
|
||||
private function parseObjectAnimationScale(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
animation.scale = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
/**
|
||||
* Объект-контейнер, содержащий все загруженные объекты.
|
||||
*/
|
||||
public function get content():Vector.<Mesh> {
|
||||
return _content;
|
||||
}
|
||||
|
||||
// Считываем строку заканчивающуюся на нулевой байт
|
||||
private function getString(index:uint):String {
|
||||
data.position = index;
|
||||
var charCode:uint = data.readByte();
|
||||
var res:String = "";
|
||||
while (charCode != 0) {
|
||||
res += String.fromCharCode(charCode);
|
||||
charCode = data.readByte();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private function getRotationFrom3DSAngleAxis(angle:Number, x:Number, z:Number, y:Number):Vector3D {
|
||||
var res:Vector3D = new Vector3D();
|
||||
var s:Number = Math.sin(angle);
|
||||
var c:Number = Math.cos(angle);
|
||||
var t:Number = 1 - c;
|
||||
var k:Number = x*y*t + z*s;
|
||||
var half:Number;
|
||||
if (k > 0.998) {
|
||||
half = angle/2;
|
||||
res.z = -2*Math.atan2(x*Math.sin(half), Math.cos(half));
|
||||
res.y = -Math.PI/2;
|
||||
res.x = 0;
|
||||
return res;
|
||||
}
|
||||
if (k < -0.998) {
|
||||
half = angle/2;
|
||||
res.z = 2*Math.atan2(x*Math.sin(half), Math.cos(half));
|
||||
res.y = Math.PI/2;
|
||||
res.x = 0;
|
||||
return res;
|
||||
}
|
||||
res.z = -Math.atan2(y*s - x*z*t, 1 - (y*y + z*z)*t);
|
||||
res.y = -Math.asin(x*y*t + z*s);
|
||||
res.x = -Math.atan2(x*s - y*z*t, 1 - (x*x + z*z)*t);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
class MaterialData {
|
||||
public var name:String;
|
||||
public var color:uint;
|
||||
public var specular:uint;
|
||||
public var glossiness:uint;
|
||||
public var transparency:uint;
|
||||
public var diffuseMap:MapData;
|
||||
public var opacityMap:MapData;
|
||||
//public var normalMap:MapData;
|
||||
public var matrix:Matrix;
|
||||
}
|
||||
|
||||
class MapData {
|
||||
public var filename:String;
|
||||
public var scaleU:Number = 1;
|
||||
public var scaleV:Number = 1;
|
||||
public var offsetU:Number = 0;
|
||||
public var offsetV:Number = 0;
|
||||
public var rotation:Number = 0;
|
||||
}
|
||||
|
||||
class ObjectData {
|
||||
public var name:String;
|
||||
public var vertices:Vector.<Vector3D>;
|
||||
public var uvs:Vector.<Point>;
|
||||
public var matrix:Matrix3D;
|
||||
public var faces:Array;
|
||||
public var surfaces:Object;
|
||||
}
|
||||
|
||||
class FaceData {
|
||||
public var a:uint;
|
||||
public var b:uint;
|
||||
public var c:uint;
|
||||
}
|
||||
|
||||
class SurfaceData {
|
||||
public var materialName:String;
|
||||
public var faces:Array;
|
||||
}
|
||||
|
||||
class AnimationData {
|
||||
public var objectName:String;
|
||||
public var object:Mesh;
|
||||
public var parentIndex:uint;
|
||||
public var pivot:Vector3D;
|
||||
public var position:Vector3D;
|
||||
public var rotation:Vector3D;
|
||||
public var scale:Vector3D;
|
||||
}
|
||||
@@ -0,0 +1,812 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
import flash.display.BlendMode;
|
||||
import flash.events.Event;
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Orientation3D;
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Vector3D;
|
||||
import flash.net.URLLoader;
|
||||
import flash.system.LoaderContext;
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Endian;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class Loader3DSByteArray extends EventDispatcher {
|
||||
|
||||
private var _content:Vector.<Mesh>;
|
||||
private var version:uint;
|
||||
private var objectDatas:Object;
|
||||
private var animationDatas:Array;
|
||||
private var materialDatas:Array;
|
||||
private var bitmaps:Array;
|
||||
|
||||
private var modelLoader:URLLoader;
|
||||
private var textureLoader:TextureLoader;
|
||||
alternativa3d var data:ByteArray;
|
||||
|
||||
private var counter:int;
|
||||
private var textureMaterialNames:Array;
|
||||
private var context:LoaderContext;
|
||||
private var path:String;
|
||||
|
||||
/**
|
||||
* Повтор текстуры при заливке для создаваемых текстурных материалов.
|
||||
*
|
||||
* @see alternativa.engine3d.materials.TextureMaterial
|
||||
*/
|
||||
public var repeat:Boolean = true;
|
||||
/**
|
||||
* Сглаживание текстур при увеличении масштаба.
|
||||
*
|
||||
* @see alternativa.engine3d.materials.TextureMaterial
|
||||
*/
|
||||
public var smooth:Boolean = false;
|
||||
/**
|
||||
* Режим наложения цвета для создаваемых текстурных материалов.
|
||||
*
|
||||
* @see alternativa.engine3d.materials.TextureMaterial
|
||||
*/
|
||||
public var blendMode:String = BlendMode.NORMAL;
|
||||
|
||||
/**
|
||||
* Коэффициент пересчёта единиц измерения модели.
|
||||
*/
|
||||
public var units:Number = 1;
|
||||
/**
|
||||
* Устанавливаемый уровень мобильности загруженных объектов.
|
||||
*/
|
||||
public var mobility:int = 0;
|
||||
|
||||
private function clean():void {
|
||||
_content = null;
|
||||
objectDatas = null;
|
||||
animationDatas = null;
|
||||
materialDatas = null;
|
||||
bitmaps = null;
|
||||
textureMaterialNames = null;
|
||||
}
|
||||
|
||||
public function parseByteArray(data:ByteArray):void {
|
||||
this.data = data;
|
||||
this.data.endian = Endian.LITTLE_ENDIAN;
|
||||
parse3DSChunk(0, this.data.bytesAvailable);
|
||||
}
|
||||
|
||||
private function buildContent():void {
|
||||
var i:uint;
|
||||
var length:uint;
|
||||
|
||||
// Формируем связи объектов
|
||||
_content = new Vector.<Mesh>();
|
||||
|
||||
// Создаём материалы
|
||||
var materialData:MaterialData;
|
||||
for (var materialName:String in materialDatas) {
|
||||
materialData = materialDatas[materialName];
|
||||
var mapData:MapData = materialData.diffuseMap;
|
||||
var materialMatrix:Matrix = new Matrix();
|
||||
if (mapData != null) {
|
||||
var rot:Number = mapData.rotation*Math.PI/180;
|
||||
var rotSin:Number = Math.sin(rot);
|
||||
var rotCos:Number = Math.cos(rot);
|
||||
materialMatrix.translate(-mapData.offsetU, mapData.offsetV);
|
||||
materialMatrix.translate(-0.5, -0.5);
|
||||
materialMatrix.rotate(-rot);
|
||||
materialMatrix.scale(mapData.scaleU, mapData.scaleV);
|
||||
materialMatrix.translate(0.5, 0.5);
|
||||
}
|
||||
materialData.matrix = materialMatrix;
|
||||
}
|
||||
|
||||
// Если есть данные об анимации и иерархии объектов
|
||||
var objectName:String;
|
||||
var objectData:ObjectData;
|
||||
var mesh:Mesh;
|
||||
if (animationDatas != null) {
|
||||
if (objectDatas != null) {
|
||||
|
||||
length = animationDatas.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var animationData:AnimationData = animationDatas[i];
|
||||
objectName = animationData.objectName;
|
||||
objectData = objectDatas[objectName];
|
||||
|
||||
// Если на один объект приходится несколько данных об анимации
|
||||
if (objectData != null) {
|
||||
var nameCounter:uint = 2;
|
||||
for (var j:uint = i + 1; j < length; j++) {
|
||||
var animationData2:AnimationData = animationDatas[j];
|
||||
if (objectName == animationData2.objectName) {
|
||||
var newName:String = objectName + nameCounter;
|
||||
var newObjectData:ObjectData = new ObjectData();
|
||||
animationData2.objectName = newName;
|
||||
newObjectData.name = newName;
|
||||
if (objectData.vertices != null) {
|
||||
newObjectData.vertices = new Vector.<Vector3D>().concat(objectData.vertices);
|
||||
}
|
||||
if (objectData.uvs != null) {
|
||||
newObjectData.uvs = new new Vector.<Point>().concat(objectData.uvs);
|
||||
}
|
||||
if (objectData.matrix != null) {
|
||||
newObjectData.matrix = objectData.matrix.clone();
|
||||
}
|
||||
if (objectData.faces != null) {
|
||||
newObjectData.faces = new Array().concat(objectData.faces);
|
||||
}
|
||||
if (objectData.surfaces != null) {
|
||||
newObjectData.surfaces = objectData.surfaces.clone();
|
||||
}
|
||||
objectDatas[newName] = newObjectData;
|
||||
nameCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (objectData != null && objectData.vertices != null) {
|
||||
// Меш
|
||||
mesh = new Mesh();
|
||||
mesh.createEmptyGeometry(objectData.vertices.length, objectData.faces.length);
|
||||
animationData.object = mesh;
|
||||
buildObject(animationData);
|
||||
buildMesh(mesh, objectData, animationData);
|
||||
_content.push(mesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (objectName in objectDatas) {
|
||||
objectData = objectDatas[objectName];
|
||||
if (objectData.vertices != null) {
|
||||
// Меш
|
||||
mesh = new Mesh();
|
||||
mesh.createEmptyGeometry(objectData.vertices.length, objectData.faces.length);
|
||||
buildMesh(mesh, objectData, null);
|
||||
_content.push(mesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Рассылаем событие о завершении
|
||||
dispatchEvent(new Event(Event.COMPLETE));
|
||||
}
|
||||
|
||||
private function buildObject(animationData:AnimationData):void {
|
||||
var object:Mesh = animationData.object;
|
||||
var transform:Vector.<Vector3D> = new Vector.<Vector3D>(3, true);
|
||||
transform[0] = (animationData.position == null) ? new Vector3D() : new Vector3D(animationData.position.x * units, animationData.position.y * units, animationData.position.z * units);
|
||||
transform[1] = (animationData.rotation == null) ? new Vector3D() : animationData.rotation.clone();
|
||||
transform[2] = (animationData.scale == null) ? new Vector3D(1, 1, 1) : animationData.scale.clone();
|
||||
object.matrix.recompose(transform, Orientation3D.AXIS_ANGLE);
|
||||
}
|
||||
|
||||
private function buildMesh(mesh:Mesh, objectData:ObjectData, animationData:AnimationData):void {
|
||||
|
||||
// Добавляем вершины
|
||||
var i:uint;
|
||||
var j:uint;
|
||||
var key:*;
|
||||
var length:uint = objectData.vertices.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var vertexData:Vector3D = objectData.vertices[i];
|
||||
var uv:Point = objectData.uvs[i];
|
||||
j = i*3;
|
||||
mesh.vertices[j] = vertexData.x;
|
||||
mesh.vertices[j + 1] = vertexData.y;
|
||||
mesh.vertices[j + 2] = vertexData.z;
|
||||
mesh.uvts[j] = uv.x;
|
||||
mesh.uvts[j + 1] = 1 - uv.y;
|
||||
}
|
||||
|
||||
// Коррекция вершин
|
||||
if (animationData != null) {
|
||||
// Инвертируем матрицу
|
||||
objectData.matrix.invert();
|
||||
|
||||
// Вычитаем точку привязки из смещения матрицы
|
||||
if (animationData.pivot != null) {
|
||||
objectData.matrix.appendTranslation(-animationData.pivot.x, -animationData.pivot.y, -animationData.pivot.z);
|
||||
}
|
||||
|
||||
// Трансформируем вершины
|
||||
objectData.matrix.transformVectors(mesh.vertices, mesh.vertices);
|
||||
}
|
||||
for (i = 0; i < mesh.numVertices*3; i++) {
|
||||
mesh.vertices[i] *= units;
|
||||
}
|
||||
|
||||
// Добавляем грани
|
||||
length = objectData.faces.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var faceData:FaceData = objectData.faces[i];
|
||||
j = i*3;
|
||||
mesh.indices[j] = faceData.a;
|
||||
mesh.indices[j + 1] = faceData.b;
|
||||
mesh.indices[j + 2] = faceData.c;
|
||||
}
|
||||
|
||||
// Добавляем поверхности
|
||||
/*if (objectData.surfaces != null) {
|
||||
for (var surfaceId:String in objectData.surfaces) {
|
||||
var materialData:MaterialData = materialDatas[surfaceId];
|
||||
if (materialData.diffuseMap != null) {
|
||||
mesh.texture = bitmaps[materialData.name];
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
alternativa3d function parse3DSChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Главный
|
||||
case 0x4D4D:
|
||||
parseMainChunk(dataIndex, dataLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parse3DSChunk(index + chunkLength, length - chunkLength);
|
||||
} else {
|
||||
// Загрузка битмап
|
||||
//loadBitmaps();
|
||||
buildContent(); // Без подгрузки битмап
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMainChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Версия
|
||||
case 0x0002:
|
||||
parseVersion(dataIndex);
|
||||
break;
|
||||
// 3D-сцена
|
||||
case 0x3D3D:
|
||||
parse3DChunk(dataIndex, dataLength);
|
||||
break;
|
||||
// Анимация
|
||||
case 0xB000:
|
||||
parseAnimationChunk(dataIndex, dataLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parseMainChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseVersion(index:uint):void {
|
||||
data.position = index;
|
||||
version = data.readUnsignedInt();
|
||||
}
|
||||
|
||||
private function parse3DChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Материал
|
||||
case 0xAFFF:
|
||||
// Парсим материал
|
||||
var material:MaterialData = new MaterialData();
|
||||
parseMaterialChunk(material, dataIndex, dataLength);
|
||||
break;
|
||||
// Объект
|
||||
case 0x4000:
|
||||
// Создаём данные объекта
|
||||
var object:ObjectData = new ObjectData();
|
||||
var objectLength:uint = parseObject(object, dataIndex);
|
||||
// Парсим объект
|
||||
parseObjectChunk(object, dataIndex + objectLength, dataLength - objectLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parse3DChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMaterialChunk(material:MaterialData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Имя материала
|
||||
case 0xA000:
|
||||
parseMaterialName(material, dataIndex);
|
||||
break;
|
||||
// Ambient color
|
||||
case 0xA010:
|
||||
break;
|
||||
// Diffuse color
|
||||
case 0xA020:
|
||||
data.position = dataIndex + 6;
|
||||
material.color = (data.readUnsignedByte() << 16) + (data.readUnsignedByte() << 8) + data.readUnsignedByte();
|
||||
break;
|
||||
// Specular color
|
||||
case 0xA030:
|
||||
break;
|
||||
// Shininess percent
|
||||
case 0xA040:
|
||||
data.position = dataIndex + 6;
|
||||
material.glossiness = data.readUnsignedShort();
|
||||
break;
|
||||
// Shininess strength percent
|
||||
case 0xA041:
|
||||
data.position = dataIndex + 6;
|
||||
material.specular = data.readUnsignedShort();
|
||||
break;
|
||||
// Transperensy
|
||||
case 0xA050:
|
||||
data.position = dataIndex + 6;
|
||||
material.transparency = data.readUnsignedShort();
|
||||
break;
|
||||
// Texture map 1
|
||||
case 0xA200:
|
||||
material.diffuseMap = new MapData();
|
||||
parseMapChunk(material.name, material.diffuseMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Texture map 2
|
||||
case 0xA33A:
|
||||
break;
|
||||
// Opacity map
|
||||
case 0xA210:
|
||||
material.opacityMap = new MapData();
|
||||
parseMapChunk(material.name, material.opacityMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Bump map
|
||||
case 0xA230:
|
||||
//material.normalMap = new MapData();
|
||||
//parseMapChunk(material.normalMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Shininess map
|
||||
case 0xA33C:
|
||||
break;
|
||||
// Specular map
|
||||
case 0xA204:
|
||||
break;
|
||||
// Self-illumination map
|
||||
case 0xA33D:
|
||||
break;
|
||||
// Reflection map
|
||||
case 0xA220:
|
||||
break;
|
||||
}
|
||||
|
||||
parseMaterialChunk(material, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMaterialName(material:MaterialData, index:uint):void {
|
||||
// Создаём список материалов, если надо
|
||||
if (materialDatas == null) {
|
||||
materialDatas = new Array();
|
||||
}
|
||||
// Получаем название материала
|
||||
material.name = getString(index);
|
||||
// Помещаем данные материала в список
|
||||
materialDatas[material.name] = material;
|
||||
}
|
||||
|
||||
private function parseMapChunk(materialName:String, map:MapData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Имя файла
|
||||
case 0xA300:
|
||||
map.filename = getString(dataIndex).toLowerCase();
|
||||
break;
|
||||
// Масштаб по U
|
||||
case 0xA354:
|
||||
data.position = dataIndex;
|
||||
map.scaleU = data.readFloat();
|
||||
break;
|
||||
// Масштаб по V
|
||||
case 0xA356:
|
||||
data.position = dataIndex;
|
||||
map.scaleV = data.readFloat();
|
||||
break;
|
||||
// Смещение по U
|
||||
case 0xA358:
|
||||
data.position = dataIndex;
|
||||
map.offsetU = data.readFloat();
|
||||
break;
|
||||
// Смещение по V
|
||||
case 0xA35A:
|
||||
data.position = dataIndex;
|
||||
map.offsetV = data.readFloat();
|
||||
break;
|
||||
// Угол поворота
|
||||
case 0xA35C:
|
||||
data.position = dataIndex;
|
||||
map.rotation = data.readFloat();
|
||||
break;
|
||||
}
|
||||
|
||||
parseMapChunk(materialName, map, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseObject(object:ObjectData, index:uint):uint {
|
||||
// Создаём список объектов, если надо
|
||||
if (objectDatas == null) {
|
||||
objectDatas = new Object();
|
||||
}
|
||||
// Получаем название объекта
|
||||
object.name = getString(index);
|
||||
// Помещаем данные объекта в список
|
||||
objectDatas[object.name] = object;
|
||||
return object.name.length + 1;
|
||||
}
|
||||
|
||||
private function parseObjectChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Меш
|
||||
case 0x4100:
|
||||
parseMeshChunk(object, dataIndex, dataLength);
|
||||
break;
|
||||
// Источник света
|
||||
case 0x4600:
|
||||
break;
|
||||
// Камера
|
||||
case 0x4700:
|
||||
break;
|
||||
}
|
||||
|
||||
parseObjectChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMeshChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Вершины
|
||||
case 0x4110:
|
||||
parseVertices(object, dataIndex);
|
||||
break;
|
||||
// UV
|
||||
case 0x4140:
|
||||
parseUVs(object, dataIndex);
|
||||
break;
|
||||
// Трансформация
|
||||
case 0x4160:
|
||||
parseMatrix(object, dataIndex);
|
||||
break;
|
||||
// Грани
|
||||
case 0x4120:
|
||||
var facesLength:uint = parseFaces(object, dataIndex);
|
||||
parseFacesChunk(object, dataIndex + facesLength, dataLength - facesLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parseMeshChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseVertices(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.vertices = new Vector.<Vector3D>();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
object.vertices.push(new Vector3D(data.readFloat(), data.readFloat(), data.readFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
private function parseUVs(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.uvs = new Vector.<Point>();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
object.uvs.push(new Point(data.readFloat(), data.readFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMatrix(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
object.matrix = new Matrix3D(Vector.<Number>([
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 0,
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 0,
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 0,
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 1
|
||||
]));
|
||||
}
|
||||
|
||||
private function parseFaces(object:ObjectData, index:uint):uint {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.faces = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
var face:FaceData = new FaceData();
|
||||
face.a = data.readUnsignedShort();
|
||||
face.b = data.readUnsignedShort();
|
||||
face.c = data.readUnsignedShort();
|
||||
object.faces.push(face);
|
||||
data.position += 2; // Пропускаем флаг
|
||||
}
|
||||
return 2 + num*8;
|
||||
}
|
||||
|
||||
private function parseFacesChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Поверхности
|
||||
case 0x4130:
|
||||
parseSurface(object, dataIndex);
|
||||
break;
|
||||
// Группы сглаживания
|
||||
case 0x4150:
|
||||
break;
|
||||
}
|
||||
|
||||
parseFacesChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseSurface(object:ObjectData, index:uint):void {
|
||||
// Создаём данные поверхности
|
||||
var surface:SurfaceData = new SurfaceData();
|
||||
// Создаём список поверхностей, если надо
|
||||
if (object.surfaces == null) {
|
||||
object.surfaces = new Object();
|
||||
}
|
||||
// Получаем название материала
|
||||
surface.materialName = getString(index);
|
||||
// Помещаем данные поверхности в список
|
||||
object.surfaces[surface.materialName] = surface;
|
||||
|
||||
// Получаем грани поверхности
|
||||
data.position = index + surface.materialName.length + 1;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
surface.faces = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
surface.faces.push(data.readUnsignedShort());
|
||||
}
|
||||
}
|
||||
|
||||
private function parseAnimationChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Анимация объекта
|
||||
case 0xB001:
|
||||
case 0xB002:
|
||||
case 0xB003:
|
||||
case 0xB004:
|
||||
case 0xB005:
|
||||
case 0xB006:
|
||||
case 0xB007:
|
||||
var animation:AnimationData = new AnimationData();
|
||||
if (animationDatas == null) {
|
||||
animationDatas = new Array();
|
||||
}
|
||||
animationDatas.push(animation);
|
||||
parseObjectAnimationChunk(animation, dataIndex, dataLength);
|
||||
break;
|
||||
|
||||
// Таймлайн
|
||||
case 0xB008:
|
||||
break;
|
||||
}
|
||||
|
||||
parseAnimationChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseObjectAnimationChunk(animation:AnimationData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Идентификация объекта и его связь
|
||||
case 0xB010:
|
||||
parseObjectAnimationInfo(animation, dataIndex);
|
||||
break;
|
||||
// Точка привязки объекта (pivot)
|
||||
case 0xB013:
|
||||
parseObjectAnimationPivot(animation, dataIndex);
|
||||
break;
|
||||
// Смещение объекта относительно родителя
|
||||
case 0xB020:
|
||||
parseObjectAnimationPosition(animation, dataIndex);
|
||||
break;
|
||||
// Поворот объекта относительно родителя (angle-axis)
|
||||
case 0xB021:
|
||||
parseObjectAnimationRotation(animation, dataIndex);
|
||||
break;
|
||||
// Масштабирование объекта относительно родителя
|
||||
case 0xB022:
|
||||
parseObjectAnimationScale(animation, dataIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
parseObjectAnimationChunk(animation, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseObjectAnimationInfo(animation:AnimationData, index:uint):void {
|
||||
var name:String = getString(index);
|
||||
data.position = index + name.length + 1 + 4;
|
||||
animation.objectName = name;
|
||||
animation.parentIndex = data.readUnsignedShort();
|
||||
}
|
||||
|
||||
private function parseObjectAnimationPivot(animation:AnimationData, index:uint):void {
|
||||
data.position = index;
|
||||
animation.pivot = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
private function parseObjectAnimationPosition(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
animation.position = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
private function parseObjectAnimationRotation(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
var angle:Number = data.readFloat();
|
||||
animation.rotation = new Vector3D(data.readFloat(), -data.readFloat(), data.readFloat(), angle);
|
||||
}
|
||||
|
||||
private function parseObjectAnimationScale(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
animation.scale = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
/**
|
||||
* Объект-контейнер, содержащий все загруженные объекты.
|
||||
*/
|
||||
public function get content():Vector.<Mesh> {
|
||||
return _content;
|
||||
}
|
||||
|
||||
// Считываем строку заканчивающуюся на нулевой байт
|
||||
private function getString(index:uint):String {
|
||||
data.position = index;
|
||||
var charCode:uint = data.readByte();
|
||||
var res:String = "";
|
||||
while (charCode != 0) {
|
||||
res += String.fromCharCode(charCode);
|
||||
charCode = data.readByte();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private function getRotationFrom3DSAngleAxis(angle:Number, x:Number, z:Number, y:Number):Vector3D {
|
||||
var res:Vector3D = new Vector3D();
|
||||
var s:Number = Math.sin(angle);
|
||||
var c:Number = Math.cos(angle);
|
||||
var t:Number = 1 - c;
|
||||
var k:Number = x*y*t + z*s;
|
||||
var half:Number;
|
||||
if (k > 0.998) {
|
||||
half = angle/2;
|
||||
res.z = -2*Math.atan2(x*Math.sin(half), Math.cos(half));
|
||||
res.y = -Math.PI/2;
|
||||
res.x = 0;
|
||||
return res;
|
||||
}
|
||||
if (k < -0.998) {
|
||||
half = angle/2;
|
||||
res.z = 2*Math.atan2(x*Math.sin(half), Math.cos(half));
|
||||
res.y = Math.PI/2;
|
||||
res.x = 0;
|
||||
return res;
|
||||
}
|
||||
res.z = -Math.atan2(y*s - x*z*t, 1 - (y*y + z*z)*t);
|
||||
res.y = -Math.asin(x*y*t + z*s);
|
||||
res.x = -Math.atan2(x*s - y*z*t, 1 - (x*x + z*z)*t);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
class MaterialData {
|
||||
public var name:String;
|
||||
public var color:uint;
|
||||
public var specular:uint;
|
||||
public var glossiness:uint;
|
||||
public var transparency:uint;
|
||||
public var diffuseMap:MapData;
|
||||
public var opacityMap:MapData;
|
||||
//public var normalMap:MapData;
|
||||
public var matrix:Matrix;
|
||||
}
|
||||
|
||||
class MapData {
|
||||
public var filename:String;
|
||||
public var scaleU:Number = 1;
|
||||
public var scaleV:Number = 1;
|
||||
public var offsetU:Number = 0;
|
||||
public var offsetV:Number = 0;
|
||||
public var rotation:Number = 0;
|
||||
}
|
||||
|
||||
class ObjectData {
|
||||
public var name:String;
|
||||
public var vertices:Vector.<Vector3D>;
|
||||
public var uvs:Vector.<Point>;
|
||||
public var matrix:Matrix3D;
|
||||
public var faces:Array;
|
||||
public var surfaces:Object;
|
||||
}
|
||||
|
||||
class FaceData {
|
||||
public var a:uint;
|
||||
public var b:uint;
|
||||
public var c:uint;
|
||||
}
|
||||
|
||||
class SurfaceData {
|
||||
public var materialName:String;
|
||||
public var faces:Array;
|
||||
}
|
||||
|
||||
class AnimationData {
|
||||
public var objectName:String;
|
||||
public var object:Mesh;
|
||||
public var parentIndex:uint;
|
||||
public var pivot:Vector3D;
|
||||
public var position:Vector3D;
|
||||
public var rotation:Vector3D;
|
||||
public var scale:Vector3D;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
|
||||
public class MaterialParams {
|
||||
|
||||
public var color:uint;
|
||||
public var opacity:Number;
|
||||
public var diffuseMap:String;
|
||||
public var opacityMap:String;
|
||||
|
||||
public function toString():String {
|
||||
return "[MaterialParams color=" + color + ", opacity=" + opacity + ", diffuseMap=" + diffuseMap + ", opacityMap=" + opacityMap + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
public class Parsed3DSData {
|
||||
|
||||
/**
|
||||
* Список объектов в порядке их появления в 3DS-данных.
|
||||
**/
|
||||
public var objects:Vector.<Object3D>;
|
||||
/**
|
||||
* Список материалов каждого объекта. Если для объекта нет назначенных материалов, соответствующий элемент списка равен null.
|
||||
**/
|
||||
public var objectMaterials:Vector.<Vector.<String>>;
|
||||
/**
|
||||
* Список материалов 3DS-файла (materialName => MaterialParams).
|
||||
*/
|
||||
public var materials:Object;
|
||||
|
||||
}
|
||||
}
|
||||
970
Alternativa3D7/7.0/alternativa/engine3d/loaders/Parser3DS.as
Normal file
970
Alternativa3D7/7.0/alternativa/engine3d/loaders/Parser3DS.as
Normal file
@@ -0,0 +1,970 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
import flash.display.BlendMode;
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Orientation3D;
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Vector3D;
|
||||
import flash.net.URLLoader;
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Endian;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Parser3DS extends EventDispatcher {
|
||||
|
||||
private var version:uint;
|
||||
private var objectDatas:Object;
|
||||
private var animationDatas:Vector.<AnimationData>;
|
||||
private var materialDatas:Object;
|
||||
|
||||
private var modelLoader:URLLoader;
|
||||
private var data:ByteArray;
|
||||
|
||||
/**
|
||||
* Повтор текстуры при заливке для создаваемых текстурных материалов.
|
||||
*
|
||||
* @see alternativa.engine3d.materials.TextureMaterial
|
||||
*/
|
||||
public var repeat:Boolean = true;
|
||||
/**
|
||||
* Сглаживание текстур при увеличении масштаба.
|
||||
*
|
||||
* @see alternativa.engine3d.materials.TextureMaterial
|
||||
*/
|
||||
public var smooth:Boolean = false;
|
||||
/**
|
||||
* Режим наложения цвета для создаваемых текстурных материалов.
|
||||
*
|
||||
* @see alternativa.engine3d.materials.TextureMaterial
|
||||
*/
|
||||
public var blendMode:String = BlendMode.NORMAL;
|
||||
|
||||
/**
|
||||
* Коэффициент пересчёта единиц измерения модели.
|
||||
*/
|
||||
public var units:Number = 1;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
public function Parser3DS() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
public function parse(data:ByteArray):Parsed3DSData {
|
||||
this.data = data;
|
||||
data.endian = Endian.LITTLE_ENDIAN;
|
||||
parse3DSChunk(0, data.bytesAvailable);
|
||||
return buildContent();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function clean():void {
|
||||
objectDatas = null;
|
||||
animationDatas = null;
|
||||
materialDatas = null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function buildContent():Parsed3DSData {
|
||||
var result:Parsed3DSData = new Parsed3DSData();
|
||||
var i:uint;
|
||||
var length:uint;
|
||||
|
||||
// Формируем связи объектов
|
||||
result.objects = new Vector.<Object3D>();
|
||||
result.objectMaterials = new Vector.<Vector.<String>>();
|
||||
result.materials = {};
|
||||
|
||||
// Создаём материалы
|
||||
var materialData:MaterialData;
|
||||
for (var materialName:String in materialDatas) {
|
||||
materialData = materialDatas[materialName];
|
||||
var mapData:MapData = materialData.diffuseMap;
|
||||
var materialMatrix:Matrix = new Matrix();
|
||||
if (mapData != null) {
|
||||
var rot:Number = mapData.rotation*Math.PI/180;
|
||||
var rotSin:Number = Math.sin(rot);
|
||||
var rotCos:Number = Math.cos(rot);
|
||||
materialMatrix.translate(-mapData.offsetU, mapData.offsetV);
|
||||
materialMatrix.translate(-0.5, -0.5);
|
||||
materialMatrix.rotate(-rot);
|
||||
materialMatrix.scale(mapData.scaleU, mapData.scaleV);
|
||||
materialMatrix.translate(0.5, 0.5);
|
||||
}
|
||||
materialData.matrix = materialMatrix;
|
||||
|
||||
var mat:MaterialParams = new MaterialParams();
|
||||
mat.color = materialData.color;
|
||||
mat.opacity = 1 - 0.01*materialData.transparency;
|
||||
if (materialData.diffuseMap != null) {
|
||||
mat.diffuseMap = materialData.diffuseMap.filename;
|
||||
}
|
||||
if (materialData.opacityMap != null) {
|
||||
mat.opacityMap = materialData.opacityMap.filename;
|
||||
}
|
||||
result.materials[materialName] = mat;
|
||||
}
|
||||
|
||||
// Если есть данные об анимации и иерархии объектов
|
||||
var objectName:String;
|
||||
var objectData:ObjectData;
|
||||
var mesh:Mesh;
|
||||
if (animationDatas != null) {
|
||||
if (objectDatas != null) {
|
||||
|
||||
length = animationDatas.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var animationData:AnimationData = animationDatas[i];
|
||||
objectName = animationData.objectName;
|
||||
objectData = objectDatas[objectName];
|
||||
|
||||
// Если на один объект приходится несколько данных об анимации
|
||||
if (objectData != null) {
|
||||
var nameCounter:uint = 2;
|
||||
for (var j:uint = i + 1; j < length; j++) {
|
||||
var animationData2:AnimationData = animationDatas[j];
|
||||
if (objectName == animationData2.objectName) {
|
||||
var newName:String = objectName + nameCounter;
|
||||
var newObjectData:ObjectData = new ObjectData();
|
||||
animationData2.objectName = newName;
|
||||
newObjectData.name = newName;
|
||||
if (objectData.vertices != null) {
|
||||
newObjectData.vertices = new Vector.<Vector3D>().concat(objectData.vertices);
|
||||
}
|
||||
if (objectData.uvs != null) {
|
||||
newObjectData.uvs = new Vector.<Point>().concat(objectData.uvs);
|
||||
}
|
||||
if (objectData.matrix != null) {
|
||||
newObjectData.matrix = objectData.matrix.clone();
|
||||
}
|
||||
if (objectData.faces != null) {
|
||||
newObjectData.faces = new Array().concat(objectData.faces);
|
||||
}
|
||||
if (objectData.surfaces != null) {
|
||||
newObjectData.surfaces = objectData.surfaces.clone();
|
||||
}
|
||||
objectDatas[newName] = newObjectData;
|
||||
nameCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (objectData != null && objectData.vertices != null) {
|
||||
// Меш
|
||||
mesh = new Mesh();
|
||||
mesh.createEmptyGeometry(objectData.vertices.length, objectData.faces.length);
|
||||
animationData.object = mesh;
|
||||
buildObject(animationData);
|
||||
buildMesh(mesh, objectData, animationData, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (objectName in objectDatas) {
|
||||
objectData = objectDatas[objectName];
|
||||
if (objectData.vertices != null) {
|
||||
// Меш
|
||||
mesh = new Mesh();
|
||||
mesh.createEmptyGeometry(objectData.vertices.length, objectData.faces.length);
|
||||
buildMesh(mesh, objectData, null, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clean();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param animationData
|
||||
*/
|
||||
private function buildObject(animationData:AnimationData):void {
|
||||
var object:Mesh = animationData.object;
|
||||
var transform:Vector.<Vector3D> = new Vector.<Vector3D>(3, true);
|
||||
transform[0] = (animationData.position == null) ? new Vector3D() : new Vector3D(animationData.position.x*units, animationData.position.y*units, animationData.position.z*units);
|
||||
transform[1] = (animationData.rotation == null) ? new Vector3D() : animationData.rotation.clone();
|
||||
transform[2] = (animationData.scale == null) ? new Vector3D(1, 1, 1) : animationData.scale.clone();
|
||||
// trace("transform[2]", transform[2]);
|
||||
object.matrix.recompose(transform, Orientation3D.AXIS_ANGLE);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mesh
|
||||
* @param objectData
|
||||
* @param animationData
|
||||
*/
|
||||
private function buildMesh(mesh:Mesh, objectData:ObjectData, animationData:AnimationData, parsedData:Parsed3DSData):void {
|
||||
mesh.name = objectData.name;
|
||||
// Добавляем вершины
|
||||
var i:uint;
|
||||
var j:uint;
|
||||
var key:*;
|
||||
var length:uint = objectData.vertices.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var vertexData:Vector3D = objectData.vertices[i];
|
||||
var uv:Point = objectData.uvs[i];
|
||||
j = i*3;
|
||||
mesh.vertices[j] = vertexData.x;
|
||||
mesh.vertices[j + 1] = vertexData.y;
|
||||
mesh.vertices[j + 2] = vertexData.z;
|
||||
mesh.uvts[j] = uv.x;
|
||||
mesh.uvts[j + 1] = 1 - uv.y;
|
||||
}
|
||||
|
||||
// Коррекция вершин
|
||||
if (animationData != null) {
|
||||
// Инвертируем матрицу
|
||||
objectData.matrix.invert();
|
||||
|
||||
// Вычитаем точку привязки из смещения матрицы
|
||||
if (animationData.pivot != null) {
|
||||
objectData.matrix.appendTranslation(-animationData.pivot.x, -animationData.pivot.y, -animationData.pivot.z);
|
||||
}
|
||||
|
||||
// Трансформируем вершины
|
||||
objectData.matrix.transformVectors(mesh.vertices, mesh.vertices);
|
||||
}
|
||||
for (i = 0; i < mesh.numVertices*3; i++) {
|
||||
mesh.vertices[i] *= units;
|
||||
}
|
||||
|
||||
// Добавляем грани
|
||||
length = objectData.faces.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var faceData:FaceData = objectData.faces[i];
|
||||
j = i*3;
|
||||
mesh.indices[j] = faceData.a;
|
||||
mesh.indices[j + 1] = faceData.b;
|
||||
mesh.indices[j + 2] = faceData.c;
|
||||
}
|
||||
|
||||
parsedData.objects.push(mesh);
|
||||
|
||||
// Добавляем поверхности
|
||||
if (objectData.surfaces != null) {
|
||||
var meshMaterials:Vector.<String> = new Vector.<String>();
|
||||
for (var surfaceId:String in objectData.surfaces) {
|
||||
meshMaterials.push(surfaceId);
|
||||
// var materialData:MaterialData = materialDatas[surfaceId];
|
||||
// if (materialData.diffuseMap != null) {
|
||||
// mesh.texture = bitmaps[materialData.name];
|
||||
// }
|
||||
}
|
||||
parsedData.objectMaterials.push(meshMaterials);
|
||||
} else {
|
||||
parsedData.objectMaterials.push(null);
|
||||
}
|
||||
// trace("mesh.matrix.decompose()", mesh.matrix.decompose());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parse3DSChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Главный
|
||||
case 0x4D4D:
|
||||
parseMainChunk(dataIndex, dataLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parse3DSChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parseMainChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Версия
|
||||
case 0x0002:
|
||||
parseVersion(dataIndex);
|
||||
break;
|
||||
// 3D-сцена
|
||||
case 0x3D3D:
|
||||
parse3DChunk(dataIndex, dataLength);
|
||||
break;
|
||||
// Анимация
|
||||
case 0xB000:
|
||||
parseAnimationChunk(dataIndex, dataLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parseMainChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param index
|
||||
*/
|
||||
private function parseVersion(index:uint):void {
|
||||
data.position = index;
|
||||
version = data.readUnsignedInt();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parse3DChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Материал
|
||||
case 0xAFFF:
|
||||
// Парсим материал
|
||||
var material:MaterialData = new MaterialData();
|
||||
parseMaterialChunk(material, dataIndex, dataLength);
|
||||
break;
|
||||
// Объект
|
||||
case 0x4000:
|
||||
// Создаём данные объекта
|
||||
var object:ObjectData = new ObjectData();
|
||||
var objectLength:uint = parseObject(object, dataIndex);
|
||||
// Парсим объект
|
||||
parseObjectChunk(object, dataIndex + objectLength, dataLength - objectLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parse3DChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param material
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parseMaterialChunk(material:MaterialData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Имя материала
|
||||
case 0xA000:
|
||||
parseMaterialName(material, dataIndex);
|
||||
break;
|
||||
// Ambient color
|
||||
case 0xA010:
|
||||
break;
|
||||
// Diffuse color
|
||||
case 0xA020:
|
||||
data.position = dataIndex + 6;
|
||||
material.color = (data.readUnsignedByte() << 16) + (data.readUnsignedByte() << 8) + data.readUnsignedByte();
|
||||
break;
|
||||
// Specular color
|
||||
case 0xA030:
|
||||
break;
|
||||
// Shininess percent
|
||||
case 0xA040:
|
||||
data.position = dataIndex + 6;
|
||||
material.glossiness = data.readUnsignedShort();
|
||||
break;
|
||||
// Shininess strength percent
|
||||
case 0xA041:
|
||||
data.position = dataIndex + 6;
|
||||
material.specular = data.readUnsignedShort();
|
||||
break;
|
||||
// Transperensy
|
||||
case 0xA050:
|
||||
data.position = dataIndex + 6;
|
||||
material.transparency = data.readUnsignedShort();
|
||||
break;
|
||||
// Texture map 1
|
||||
case 0xA200:
|
||||
material.diffuseMap = new MapData();
|
||||
parseMapChunk(material.name, material.diffuseMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Texture map 2
|
||||
case 0xA33A:
|
||||
break;
|
||||
// Opacity map
|
||||
case 0xA210:
|
||||
material.opacityMap = new MapData();
|
||||
parseMapChunk(material.name, material.opacityMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Bump map
|
||||
case 0xA230:
|
||||
//material.normalMap = new MapData();
|
||||
//parseMapChunk(material.normalMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Shininess map
|
||||
case 0xA33C:
|
||||
break;
|
||||
// Specular map
|
||||
case 0xA204:
|
||||
break;
|
||||
// Self-illumination map
|
||||
case 0xA33D:
|
||||
break;
|
||||
// Reflection map
|
||||
case 0xA220:
|
||||
break;
|
||||
}
|
||||
|
||||
parseMaterialChunk(material, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param material
|
||||
* @param index
|
||||
*/
|
||||
private function parseMaterialName(material:MaterialData, index:uint):void {
|
||||
// Создаём список материалов, если надо
|
||||
if (materialDatas == null) {
|
||||
materialDatas = {};
|
||||
}
|
||||
// Получаем название материала
|
||||
material.name = getString(index);
|
||||
// Помещаем данные материала в список
|
||||
materialDatas[material.name] = material;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param materialName
|
||||
* @param map
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parseMapChunk(materialName:String, map:MapData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Имя файла
|
||||
case 0xA300:
|
||||
map.filename = getString(dataIndex).toLowerCase();
|
||||
break;
|
||||
// Масштаб по U
|
||||
case 0xA354:
|
||||
data.position = dataIndex;
|
||||
map.scaleU = data.readFloat();
|
||||
break;
|
||||
// Масштаб по V
|
||||
case 0xA356:
|
||||
data.position = dataIndex;
|
||||
map.scaleV = data.readFloat();
|
||||
break;
|
||||
// Смещение по U
|
||||
case 0xA358:
|
||||
data.position = dataIndex;
|
||||
map.offsetU = data.readFloat();
|
||||
break;
|
||||
// Смещение по V
|
||||
case 0xA35A:
|
||||
data.position = dataIndex;
|
||||
map.offsetV = data.readFloat();
|
||||
break;
|
||||
// Угол поворота
|
||||
case 0xA35C:
|
||||
data.position = dataIndex;
|
||||
map.rotation = data.readFloat();
|
||||
break;
|
||||
}
|
||||
|
||||
parseMapChunk(materialName, map, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
private function parseObject(object:ObjectData, index:uint):uint {
|
||||
// Создаём список объектов, если надо
|
||||
if (objectDatas == null) {
|
||||
objectDatas = new Object();
|
||||
}
|
||||
// Получаем название объекта
|
||||
object.name = getString(index);
|
||||
// Помещаем данные объекта в список
|
||||
objectDatas[object.name] = object;
|
||||
return object.name.length + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parseObjectChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Меш
|
||||
case 0x4100:
|
||||
parseMeshChunk(object, dataIndex, dataLength);
|
||||
break;
|
||||
// Источник света
|
||||
case 0x4600:
|
||||
break;
|
||||
// Камера
|
||||
case 0x4700:
|
||||
break;
|
||||
}
|
||||
|
||||
parseObjectChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parseMeshChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Вершины
|
||||
case 0x4110:
|
||||
parseVertices(object, dataIndex);
|
||||
break;
|
||||
// UV
|
||||
case 0x4140:
|
||||
parseUVs(object, dataIndex);
|
||||
break;
|
||||
// Трансформация
|
||||
case 0x4160:
|
||||
parseMatrix(object, dataIndex);
|
||||
break;
|
||||
// Грани
|
||||
case 0x4120:
|
||||
var facesLength:uint = parseFaces(object, dataIndex);
|
||||
parseFacesChunk(object, dataIndex + facesLength, dataLength - facesLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parseMeshChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @param index
|
||||
*/
|
||||
private function parseVertices(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.vertices = new Vector.<Vector3D>();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
object.vertices.push(new Vector3D(data.readFloat(), data.readFloat(), data.readFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @param index
|
||||
*/
|
||||
private function parseUVs(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.uvs = new Vector.<Point>();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
object.uvs.push(new Point(data.readFloat(), data.readFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @param index
|
||||
*/
|
||||
private function parseMatrix(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
object.matrix = new Matrix3D(Vector.<Number>([
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 0,
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 0,
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 0,
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 1
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
private function parseFaces(object:ObjectData, index:uint):uint {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.faces = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
var face:FaceData = new FaceData();
|
||||
face.a = data.readUnsignedShort();
|
||||
face.b = data.readUnsignedShort();
|
||||
face.c = data.readUnsignedShort();
|
||||
object.faces.push(face);
|
||||
data.position += 2; // Пропускаем флаг
|
||||
}
|
||||
return 2 + num*8;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parseFacesChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Поверхности
|
||||
case 0x4130:
|
||||
parseSurface(object, dataIndex);
|
||||
break;
|
||||
// Группы сглаживания
|
||||
case 0x4150:
|
||||
break;
|
||||
}
|
||||
|
||||
parseFacesChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @param index
|
||||
*/
|
||||
private function parseSurface(object:ObjectData, index:uint):void {
|
||||
// Создаём данные поверхности
|
||||
var surface:SurfaceData = new SurfaceData();
|
||||
// Создаём список поверхностей, если надо
|
||||
if (object.surfaces == null) {
|
||||
object.surfaces = new Object();
|
||||
}
|
||||
// Получаем название материала
|
||||
surface.materialName = getString(index);
|
||||
// Помещаем данные поверхности в список
|
||||
object.surfaces[surface.materialName] = surface;
|
||||
|
||||
// Получаем грани поверхности
|
||||
data.position = index + surface.materialName.length + 1;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
surface.faces = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
surface.faces.push(data.readUnsignedShort());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parseAnimationChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Анимация объекта
|
||||
case 0xB001:
|
||||
case 0xB002:
|
||||
case 0xB003:
|
||||
case 0xB004:
|
||||
case 0xB005:
|
||||
case 0xB006:
|
||||
case 0xB007:
|
||||
var animation:AnimationData = new AnimationData();
|
||||
if (animationDatas == null) {
|
||||
animationDatas = new Vector.<AnimationData>();
|
||||
}
|
||||
animationDatas.push(animation);
|
||||
parseObjectAnimationChunk(animation, dataIndex, dataLength);
|
||||
break;
|
||||
|
||||
// Таймлайн
|
||||
case 0xB008:
|
||||
break;
|
||||
}
|
||||
|
||||
parseAnimationChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param animation
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parseObjectAnimationChunk(animation:AnimationData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Идентификация объекта и его связь
|
||||
case 0xB010:
|
||||
parseObjectAnimationInfo(animation, dataIndex);
|
||||
break;
|
||||
// Точка привязки объекта (pivot)
|
||||
case 0xB013:
|
||||
parseObjectAnimationPivot(animation, dataIndex);
|
||||
break;
|
||||
// Смещение объекта относительно родителя
|
||||
case 0xB020:
|
||||
parseObjectAnimationPosition(animation, dataIndex);
|
||||
break;
|
||||
// Поворот объекта относительно родителя (angle-axis)
|
||||
case 0xB021:
|
||||
parseObjectAnimationRotation(animation, dataIndex);
|
||||
break;
|
||||
// Масштабирование объекта относительно родителя
|
||||
case 0xB022:
|
||||
parseObjectAnimationScale(animation, dataIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
parseObjectAnimationChunk(animation, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param animation
|
||||
* @param index
|
||||
*/
|
||||
private function parseObjectAnimationInfo(animation:AnimationData, index:uint):void {
|
||||
var name:String = getString(index);
|
||||
data.position = index + name.length + 1 + 4;
|
||||
animation.objectName = name;
|
||||
animation.parentIndex = data.readUnsignedShort();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param animation
|
||||
* @param index
|
||||
*/
|
||||
private function parseObjectAnimationPivot(animation:AnimationData, index:uint):void {
|
||||
data.position = index;
|
||||
animation.pivot = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param animation
|
||||
* @param index
|
||||
*/
|
||||
private function parseObjectAnimationPosition(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
animation.position = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param animation
|
||||
* @param index
|
||||
*/
|
||||
private function parseObjectAnimationRotation(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
var angle:Number = data.readFloat();
|
||||
animation.rotation = new Vector3D(-data.readFloat(), -data.readFloat(), -data.readFloat(), angle);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param animation
|
||||
* @param index
|
||||
*/
|
||||
private function parseObjectAnimationScale(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
animation.scale = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
/**
|
||||
* Считывает строку, заканчивающуюся на нулевой байт.
|
||||
*
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
private function getString(index:uint):String {
|
||||
data.position = index;
|
||||
var charCode:uint = data.readByte();
|
||||
var res:String = "";
|
||||
while (charCode != 0) {
|
||||
res += String.fromCharCode(charCode);
|
||||
charCode = data.readByte();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param angle
|
||||
* @param x
|
||||
* @param z
|
||||
* @param y
|
||||
* @return
|
||||
*/
|
||||
private function getRotationFrom3DSAngleAxis(angle:Number, x:Number, z:Number, y:Number):Vector3D {
|
||||
var res:Vector3D = new Vector3D();
|
||||
var s:Number = Math.sin(angle);
|
||||
var c:Number = Math.cos(angle);
|
||||
var t:Number = 1 - c;
|
||||
var k:Number = x*y*t + z*s;
|
||||
var half:Number;
|
||||
if (k >= 1) {
|
||||
half = 0.5*angle;
|
||||
res.z = -2*Math.atan2(x*Math.sin(half), Math.cos(half));
|
||||
res.y = -0.5*Math.PI;
|
||||
res.x = 0;
|
||||
return res;
|
||||
}
|
||||
if (k <= -1) {
|
||||
half = 0.5*angle;
|
||||
res.z = 2*Math.atan2(x*Math.sin(half), Math.cos(half));
|
||||
res.y = 0.5*Math.PI;
|
||||
res.x = 0;
|
||||
return res;
|
||||
}
|
||||
res.z = -Math.atan2(y*s - x*z*t, 1 - (y*y + z*z)*t);
|
||||
res.y = -Math.asin(k);
|
||||
res.x = -Math.atan2(x*s - y*z*t, 1 - (x*x + z*z)*t);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
class MaterialData {
|
||||
public var name:String;
|
||||
public var color:uint;
|
||||
public var specular:uint;
|
||||
public var glossiness:uint;
|
||||
public var transparency:uint;
|
||||
public var diffuseMap:MapData;
|
||||
public var opacityMap:MapData;
|
||||
//public var normalMap:MapData;
|
||||
public var matrix:Matrix;
|
||||
}
|
||||
|
||||
class MapData {
|
||||
public var filename:String;
|
||||
public var scaleU:Number = 1;
|
||||
public var scaleV:Number = 1;
|
||||
public var offsetU:Number = 0;
|
||||
public var offsetV:Number = 0;
|
||||
public var rotation:Number = 0;
|
||||
}
|
||||
|
||||
class ObjectData {
|
||||
public var name:String;
|
||||
public var vertices:Vector.<Vector3D>;
|
||||
public var uvs:Vector.<Point>;
|
||||
public var matrix:Matrix3D;
|
||||
public var faces:Array;
|
||||
public var surfaces:Object;
|
||||
}
|
||||
|
||||
class FaceData {
|
||||
public var a:uint;
|
||||
public var b:uint;
|
||||
public var c:uint;
|
||||
}
|
||||
|
||||
class SurfaceData {
|
||||
public var materialName:String;
|
||||
public var faces:Array;
|
||||
}
|
||||
|
||||
class AnimationData {
|
||||
public var objectName:String;
|
||||
public var object:Mesh;
|
||||
public var parentIndex:uint;
|
||||
public var pivot:Vector3D;
|
||||
public var position:Vector3D;
|
||||
public var rotation:Vector3D;
|
||||
public var scale:Vector3D;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
public class TextureFilesData {
|
||||
|
||||
public var diffuseMap:String;
|
||||
public var opacityMap:String;
|
||||
|
||||
public function TextureFilesData(diffuseMap:String, opacityMap:String) {
|
||||
this.diffuseMap = diffuseMap;
|
||||
this.opacityMap = opacityMap;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
|
||||
/**
|
||||
* Структура для хранения имён файла диффузной текстуры и файла карты прозрачности.
|
||||
*/
|
||||
public class TextureInfo {
|
||||
/**
|
||||
* Имя файла диффузной текстуры.
|
||||
*/
|
||||
public var diffuseMapFileName:String;
|
||||
/**
|
||||
* Имя файла карты прозрачности.
|
||||
*/
|
||||
public var opacityMapFileName:String;
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр.
|
||||
*
|
||||
* @param diffuseMapFileName имя файла диффузной текстуры
|
||||
* @param opacityMapFileName имя файла карты прозрачности
|
||||
*/
|
||||
public function TextureInfo(diffuseMapFileName:String = null, opacityMapFileName:String = null) {
|
||||
this.diffuseMapFileName = diffuseMapFileName;
|
||||
this.opacityMapFileName = opacityMapFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт строковое представление объекта.
|
||||
*
|
||||
* @return строковое представление объекта
|
||||
*/
|
||||
public function toString():String {
|
||||
return "[TextureInfo diffuseMapFileName=" + diffuseMapFileName + ", opacityMapFileName=" + opacityMapFileName + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
268
Alternativa3D7/7.0/alternativa/engine3d/loaders/TextureLoader.as
Normal file
268
Alternativa3D7/7.0/alternativa/engine3d/loaders/TextureLoader.as
Normal file
@@ -0,0 +1,268 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
import alternativa.engine3d.loaders.events.LoaderEvent;
|
||||
import alternativa.engine3d.loaders.events.LoaderProgressEvent;
|
||||
|
||||
import flash.display.Bitmap;
|
||||
import flash.display.BitmapData;
|
||||
import flash.display.BitmapDataChannel;
|
||||
import flash.display.BlendMode;
|
||||
import flash.display.Loader;
|
||||
import flash.display.LoaderInfo;
|
||||
import flash.events.Event;
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.events.ProgressEvent;
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Point;
|
||||
import flash.net.URLRequest;
|
||||
import flash.system.LoaderContext;
|
||||
|
||||
/**
|
||||
* Событие посылается, когда начинается загрузка ресурса.
|
||||
*
|
||||
* @eventType flash.events.Event.OPEN
|
||||
*/
|
||||
[Event (name="open", type="flash.events.Event")]
|
||||
/**
|
||||
* Событие посылается, когда загрузка ресурса успешно завершена.
|
||||
*
|
||||
* @eventType flash.events.Event.COMPLETE
|
||||
*/
|
||||
[Event (name="complete", type="flash.events.Event")]
|
||||
/**
|
||||
* Событие посылается при возникновении ошибки загрузки.
|
||||
*
|
||||
* @eventType flash.events.IOErrorEvent.IO_ERROR
|
||||
*/
|
||||
[Event (name="ioError", type="flash.events.IOErrorEvent")]
|
||||
/**
|
||||
* Событие посылается, когда начинается загрузка очередной части ресурса.
|
||||
*
|
||||
* @eventType alternativa.engine3d.loaders.events.LoaderEvent.PART_OPEN
|
||||
*/
|
||||
[Event (name="partOpen", type="alternativa.engine3d.loaders.events.LoaderEvent")]
|
||||
/**
|
||||
* Событие посылается, когда загрузка очередной части ресурса успешно завершена.
|
||||
*
|
||||
* @eventType alternativa.engine3d.loaders.events.LoaderEvent.PART_COMPLETE
|
||||
*/
|
||||
[Event (name="partComplete", type="alternativa.engine3d.loaders.events.LoaderEvent")]
|
||||
/**
|
||||
* Событие посылается для отображения прогресса загрузки.
|
||||
*
|
||||
* @eventType alternativa.engine3d.loaders.events.LoaderProgressEvent.LOADER_PROGRESS
|
||||
*/
|
||||
[Event (name="loaderProgress", type="alternativa.engine3d.loaders.events.LoaderProgressEvent")]
|
||||
|
||||
/**
|
||||
* Загрузчик текстуры, состоящей из одного или двух файлов. В случае, если указан второй файл, он используется для заполнения альфа-канала
|
||||
* получаемой текстуры.
|
||||
*/
|
||||
public class TextureLoader extends EventDispatcher {
|
||||
|
||||
private static const IDLE:int = -1;
|
||||
private static const LOADING_DIFFUSE_MAP:int = 0;
|
||||
private static const LOADING_ALPHA_MAP:int = 1;
|
||||
|
||||
private var state:int = IDLE;
|
||||
private var bitmapLoader:Loader;
|
||||
private var loaderContext:LoaderContext;
|
||||
private var alphaTextureUrl:String;
|
||||
private var _bitmapData:BitmapData;
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр. Если указан URL диффузной части текстуры, то сразу начинается загрузка.
|
||||
*
|
||||
* @param diffuseTextureUrl URL диффузной части текстуры
|
||||
* @param alphaTextureUrl URL карты прозрачности
|
||||
* @param loaderContext LoaderContext, используемый при загрузке
|
||||
*/
|
||||
public function TextureLoader() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Загруженная текстура.
|
||||
*/
|
||||
public function get bitmapData():BitmapData {
|
||||
return _bitmapData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузка текстурных карт. При успешной загрузке посылается сообщение <code>Event.COMPLETE</code>.
|
||||
*
|
||||
* @param diffuseTextureUrl URL файла диффузной карты
|
||||
* @param alphaTextureUrl URL файла карты прозрачности
|
||||
* @param loaderContext LoaderContext, используемый при загрузке
|
||||
*/
|
||||
public function load(diffuseTextureUrl:String, alphaTextureUrl:String = null, loaderContext:LoaderContext = null):void {
|
||||
unload();
|
||||
this.alphaTextureUrl = alphaTextureUrl == "" ? null : alphaTextureUrl;
|
||||
this.loaderContext = loaderContext;
|
||||
|
||||
loadPart(LOADING_DIFFUSE_MAP, diffuseTextureUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Прекращвет текущую загрузку. Если нет активных загрузок, не происходит ничего.
|
||||
*/
|
||||
public function close():void {
|
||||
if (state == IDLE) return;
|
||||
state = IDLE;
|
||||
bitmapLoader.unload();
|
||||
destroyLoader();
|
||||
alphaTextureUrl = null;
|
||||
loaderContext = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает внутренние ссылки на загруженные объекты, чтобы сборщик мусора смог их удалить.
|
||||
*/
|
||||
public function unload():void {
|
||||
close();
|
||||
_bitmapData = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает временные внутренние ссылки.
|
||||
*/
|
||||
private function cleanup():void {
|
||||
destroyLoader();
|
||||
alphaTextureUrl = null;
|
||||
loaderContext = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Запускает загрузку части текстуры.
|
||||
*
|
||||
* @param state фаза загрузки
|
||||
* @param url URL загружаемого файла
|
||||
*/
|
||||
private function loadPart(state:int, url:String):void {
|
||||
this.state = state;
|
||||
createLoader();
|
||||
bitmapLoader.load(new URLRequest(url), loaderContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Обрабатывает начало загрузки очередной части текстуры.
|
||||
*/
|
||||
private function onPartLoadingOpen(e:Event):void {
|
||||
if (_bitmapData == null && hasEventListener(Event.OPEN)) {
|
||||
dispatchEvent(new Event(Event.OPEN));
|
||||
}
|
||||
if (hasEventListener(LoaderEvent.PART_OPEN)) {
|
||||
dispatchEvent(new LoaderEvent(LoaderEvent.PART_OPEN, 2, state == LOADING_DIFFUSE_MAP ? 0 : 1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function onPartLoadingProgress(e:ProgressEvent):void {
|
||||
if (hasEventListener(LoaderProgressEvent.LOADER_PROGRESS)) {
|
||||
var partNumber:int = state == LOADING_DIFFUSE_MAP ? 0 : 1;
|
||||
var totalProgress:Number = 0.5*(partNumber + e.bytesLoaded/e.bytesTotal);
|
||||
dispatchEvent(new LoaderProgressEvent(LoaderProgressEvent.LOADER_PROGRESS, 2, partNumber, totalProgress, e.bytesLoaded, e.bytesTotal));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function onPartLoadingComplete(e:Event):void {
|
||||
switch (state) {
|
||||
case LOADING_DIFFUSE_MAP: {
|
||||
// Загрузилась диффузная текстура. При необходимости загружается карта прозрачности.
|
||||
_bitmapData = Bitmap(bitmapLoader.content).bitmapData;
|
||||
destroyLoader();
|
||||
dispatchPartComplete(0);
|
||||
if (alphaTextureUrl != null) {
|
||||
loadPart(LOADING_ALPHA_MAP, alphaTextureUrl);
|
||||
} else {
|
||||
complete();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LOADING_ALPHA_MAP: {
|
||||
// Загрузилась карта прозрачности. Выполняется копирование прозрачности в альфа-канал диффузной текстуры.
|
||||
var pt:Point = new Point();
|
||||
var tmpBmd:BitmapData = _bitmapData;
|
||||
_bitmapData = new BitmapData(_bitmapData.width, _bitmapData.height);
|
||||
_bitmapData.copyPixels(tmpBmd, tmpBmd.rect, pt);
|
||||
|
||||
var alpha:BitmapData = Bitmap(bitmapLoader.content).bitmapData;
|
||||
destroyLoader();
|
||||
if (_bitmapData.width != alpha.width || _bitmapData.height != alpha.height) {
|
||||
tmpBmd.draw(alpha, new Matrix(_bitmapData.width/alpha.width, 0, 0, _bitmapData.height/alpha.height), null, BlendMode.NORMAL, null, true);
|
||||
alpha.dispose();
|
||||
alpha = tmpBmd;
|
||||
} else {
|
||||
tmpBmd.dispose();
|
||||
}
|
||||
_bitmapData.copyChannel(alpha, alpha.rect, pt, BitmapDataChannel.RED, BitmapDataChannel.ALPHA);
|
||||
alpha.dispose();
|
||||
dispatchPartComplete(1);
|
||||
complete();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт событие завершения загрузки части текстуры.
|
||||
*
|
||||
* @param partnNumber номер загруженной части текстуры
|
||||
*/
|
||||
private function dispatchPartComplete(partNumber:int):void {
|
||||
if (hasEventListener(LoaderEvent.PART_COMPLETE)) {
|
||||
dispatchEvent(new LoaderEvent(LoaderEvent.PART_COMPLETE, 2, partNumber));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function onLoadError(e:Event):void {
|
||||
state = IDLE;
|
||||
cleanup();
|
||||
dispatchEvent(e);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function complete():void {
|
||||
state = IDLE;
|
||||
cleanup();
|
||||
if (hasEventListener(Event.COMPLETE)) {
|
||||
dispatchEvent(new Event(Event.COMPLETE));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function createLoader():void {
|
||||
bitmapLoader = new Loader();
|
||||
var loaderInfo:LoaderInfo = bitmapLoader.contentLoaderInfo;
|
||||
loaderInfo.addEventListener(Event.OPEN, onPartLoadingOpen);
|
||||
loaderInfo.addEventListener(ProgressEvent.PROGRESS, onPartLoadingProgress);
|
||||
loaderInfo.addEventListener(Event.COMPLETE, onPartLoadingComplete);
|
||||
loaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onLoadError);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyLoader():void {
|
||||
if (bitmapLoader == null) return;
|
||||
bitmapLoader.unload();
|
||||
var loaderInfo:LoaderInfo = bitmapLoader.contentLoaderInfo;
|
||||
loaderInfo.removeEventListener(Event.OPEN, onPartLoadingOpen);
|
||||
loaderInfo.removeEventListener(ProgressEvent.PROGRESS, onPartLoadingProgress);
|
||||
loaderInfo.removeEventListener(Event.COMPLETE, onPartLoadingComplete);
|
||||
loaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
|
||||
bitmapLoader = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package alternativa.engine3d.loaders.events {
|
||||
import flash.events.ErrorEvent;
|
||||
import flash.events.Event;
|
||||
|
||||
/**
|
||||
* Класс представляет событие ошибки, генерируемое пакетным загрузчиком текстур.
|
||||
*/
|
||||
public class BatchTextureLoaderErrorEvent extends ErrorEvent {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static const LOADER_ERROR:String = "loaderError";
|
||||
|
||||
// Имя текстуры, с которой произошла проблема
|
||||
private var _textureName:String;
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр.
|
||||
*
|
||||
* @param type тип события
|
||||
* @param textureName имя текстуры, с которой произошла проблема
|
||||
* @param text описание ошибки
|
||||
*/
|
||||
public function BatchTextureLoaderErrorEvent(type:String, textureName:String, text:String) {
|
||||
super(type);
|
||||
this.text = text;
|
||||
_textureName = textureName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Имя текстуры, с которой произошла проблема.
|
||||
*/
|
||||
public function get textureName():String {
|
||||
return _textureName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Клонирует объект.
|
||||
*
|
||||
* @return клон объекта
|
||||
*/
|
||||
override public function clone():Event {
|
||||
return new BatchTextureLoaderErrorEvent(type, _textureName, text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт строкове представление объекта.
|
||||
*
|
||||
* @return строкове представление объекта
|
||||
*/
|
||||
override public function toString():String {
|
||||
return "[BatchTextureLoaderErrorEvent textureName=" + _textureName + ", text=" + text + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package alternativa.engine3d.loaders.events {
|
||||
import flash.events.Event;
|
||||
|
||||
/**
|
||||
* Событие загрузчиков ресурсов, состоящих из нескольких частей.
|
||||
*/
|
||||
public class LoaderEvent extends Event {
|
||||
/**
|
||||
* Событие начала загрузки очередной части ресурса.
|
||||
*/
|
||||
public static const PART_OPEN:String = "partOpen";
|
||||
/**
|
||||
* Событие окончания загрузки очередной части ресурса.
|
||||
*/
|
||||
public static const PART_COMPLETE:String = "partComplete";
|
||||
|
||||
// Общее количество загружаемых частей
|
||||
private var _partsTotal:int;
|
||||
// Номер загружаемой в настоящий момент части. Нумерация начинается с нуля.
|
||||
private var _currentPart:int;
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр.
|
||||
*
|
||||
* @param type тип события
|
||||
* @param totalParts общее количество загружаемых частей
|
||||
* @param currentPart номер части, к которой относится событие. Нумерация начинается с нуля
|
||||
*/
|
||||
public function LoaderEvent(type:String, partsTotal:int, currentPart:int) {
|
||||
super(type);
|
||||
_partsTotal = partsTotal;
|
||||
_currentPart= currentPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Общее количество загружаемых частей.
|
||||
*/
|
||||
public function get partsTotal():int {
|
||||
return _partsTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Номер загружаемой в настоящий момент части. Нумерация начинается с нуля.
|
||||
*/
|
||||
public function get currentPart():int {
|
||||
return _currentPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Клонирует объект.
|
||||
*
|
||||
* @return клон объекта
|
||||
*/
|
||||
override public function clone():Event {
|
||||
return new LoaderEvent(type, _partsTotal, _currentPart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт строкове представление объекта.
|
||||
*
|
||||
* @return строкове представление объекта
|
||||
*/
|
||||
override public function toString():String {
|
||||
return "[LoaderEvent type=" + type + ", partsTotal=" + _partsTotal + ", currentPart=" + _currentPart + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package alternativa.engine3d.loaders.events {
|
||||
import flash.events.Event;
|
||||
import flash.events.ProgressEvent;
|
||||
|
||||
/**
|
||||
* Событие прогресса загрузки ресурсов, состоящих из нескольких частей.
|
||||
*/
|
||||
public class LoaderProgressEvent extends ProgressEvent {
|
||||
|
||||
/**
|
||||
* Событие прогресса загрузки очередной части ресурса.
|
||||
*/
|
||||
public static const LOADER_PROGRESS:String = "loaderProgress";
|
||||
|
||||
// Общее количество загружаемых частей
|
||||
private var _partsTotal:int;
|
||||
// Номер загружаемой в настоящий момент части. Нумерация начинается с нуля.
|
||||
private var _currentPart:int;
|
||||
// Общий прогресс загрузки, выраженный числом в интервале [0, 1]
|
||||
private var _totalProgress:Number = 0;
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр.
|
||||
*
|
||||
* @param type тип события
|
||||
* @param totalParts общее количество загружаемых частей
|
||||
* @param currentPart номер загружаемой в настоящий момент части. Нумерация начинается с нуля
|
||||
* @param totalProgress общий прогресс загрузки, выраженный числом в интервале [0, 1]
|
||||
* @param bytesLoaded количество загруженных байт текущей части
|
||||
* @param bytesTotal объём текущей части
|
||||
*/
|
||||
public function LoaderProgressEvent(type:String, partsTotal:int, currentPart:int, totalProgress:Number = 0, bytesLoaded:uint=0, bytesTotal:uint=0) {
|
||||
super(type, false, false, bytesLoaded, bytesTotal);
|
||||
_partsTotal = partsTotal;
|
||||
_currentPart= currentPart;
|
||||
_totalProgress = totalProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Общее количество загружаемых частей.
|
||||
*/
|
||||
public function get partsTotal():int {
|
||||
return _partsTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Номер загружаемой в настоящий момент части. Нумерация начинается с нуля.
|
||||
*/
|
||||
public function get currentPart():int {
|
||||
return _currentPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Общий прогресс загрузки, выраженный числом в интервале [0, 1].
|
||||
*/
|
||||
public function get totalProgress():Number {
|
||||
return _totalProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Клонирует объект.
|
||||
*
|
||||
* @return клон объекта
|
||||
*/
|
||||
override public function clone():Event {
|
||||
return new LoaderProgressEvent(type, _partsTotal, _currentPart, _totalProgress, bytesLoaded, bytesTotal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт строкове представление объекта.
|
||||
*
|
||||
* @return строкове представление объекта
|
||||
*/
|
||||
override public function toString():String {
|
||||
return "[LoaderProgressEvent partsTotal=" + _partsTotal + ", currentPart=" + _currentPart + ", totalProgress=" + _totalProgress.toFixed(2) + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package alternativa.engine3d.objects {
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Geometry;
|
||||
import alternativa.engine3d.core.MipMap;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.display.BitmapData;
|
||||
import flash.geom.Matrix3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Анимированный спрайт.
|
||||
* Анимация осуществляется путём переключения изображений,
|
||||
* хранящихся в списке textures
|
||||
*/
|
||||
public class AnimSprite extends Sprite3D {
|
||||
|
||||
/**
|
||||
* Список кадров изображений
|
||||
*/
|
||||
public var textures:Vector.<BitmapData>;
|
||||
public var mipMaps:Vector.<MipMap>;
|
||||
/**
|
||||
* Устанавливаемый кадр
|
||||
*/
|
||||
public var frame:uint = 0;
|
||||
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
if (mipMapping == 0) {
|
||||
texture = textures[frame];
|
||||
} else {
|
||||
mipMap = mipMaps[frame];
|
||||
}
|
||||
super.draw(camera, object, parentCanvas);
|
||||
}
|
||||
|
||||
override alternativa3d function debug(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
if (mipMapping == 0) {
|
||||
texture = textures[frame];
|
||||
} else {
|
||||
mipMap = mipMaps[frame];
|
||||
}
|
||||
super.debug(camera, object, parentCanvas);
|
||||
}
|
||||
|
||||
override alternativa3d function getGeometry(camera:Camera3D, object:Object3D):Geometry {
|
||||
if (mipMapping == 0) {
|
||||
texture = textures[frame];
|
||||
} else {
|
||||
mipMap = mipMaps[frame];
|
||||
}
|
||||
return super.getGeometry(camera, object);
|
||||
}
|
||||
|
||||
override public function calculateBoundBox(matrix:Matrix3D = null, boundBox:BoundBox = null):BoundBox {
|
||||
if (mipMapping == 0) {
|
||||
texture = textures[frame];
|
||||
} else {
|
||||
mipMap = mipMaps[frame];
|
||||
}
|
||||
return super.calculateBoundBox(matrix, boundBox);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
120
Alternativa3D7/7.0/alternativa/engine3d/objects/Axes.as
Normal file
120
Alternativa3D7/7.0/alternativa/engine3d/objects/Axes.as
Normal file
@@ -0,0 +1,120 @@
|
||||
package alternativa.engine3d.objects {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.display.Graphics;
|
||||
import flash.geom.Utils3D;
|
||||
import flash.text.TextField;
|
||||
import flash.text.TextFieldAutoSize;
|
||||
import flash.text.TextFormat;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Вспомогательный объект, иллюстрирующий систему коордщинат
|
||||
*/
|
||||
public class Axes extends Object3D {
|
||||
|
||||
public var axisLength:Number;
|
||||
public var lineThickness:Number;
|
||||
public var dotRadius:Number;
|
||||
public var textSize:Number;
|
||||
|
||||
public function Axes(axisLength:Number = 30, lineThickness:Number = 0, dotRadius:Number = 2, textSize:Number = 10):void {
|
||||
this.axisLength = axisLength;
|
||||
this.lineThickness = lineThickness;
|
||||
this.dotRadius = dotRadius;
|
||||
this.textSize = textSize;
|
||||
_boundBox = new BoundBox();
|
||||
_boundBox.setSize(-dotRadius, -dotRadius, -dotRadius, axisLength, axisLength, axisLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
|
||||
var p:Vector.<Number> = Vector.<Number>([0, 0, 0, axisLength, 0, 0, 0, axisLength, 0, 0, 0, axisLength]);
|
||||
var d:Vector.<Number> = new Vector.<Number>(8);
|
||||
object.cameraMatrix.transformVectors(p, p);
|
||||
|
||||
// Центр за камерой
|
||||
if (p[2] < camera.nearClipping) return;
|
||||
|
||||
Utils3D.projectVectors(camera.projectionMatrix, p, d, new Vector.<Number>());
|
||||
var size:Number = camera.viewSize/p[2];
|
||||
|
||||
// Подготовка канваса
|
||||
var canvas:Canvas = parentCanvas.getChildCanvas(true, false, object.alpha, object.blendMode, object.colorTransform, object.filters);
|
||||
|
||||
var gfx:Graphics = canvas.gfx;
|
||||
var text:TextField;
|
||||
|
||||
// Ось X
|
||||
if (p[5] >= camera.nearClipping) {
|
||||
gfx.lineStyle(lineThickness*size, 0xFF0000);
|
||||
gfx.moveTo(d[0], d[1]);
|
||||
gfx.lineTo(d[2], d[3]);
|
||||
|
||||
text = new TextField();
|
||||
text.autoSize = TextFieldAutoSize.LEFT;
|
||||
text.selectable = false;
|
||||
text.x = d[2];
|
||||
text.y = d[3];
|
||||
text.text = "X";
|
||||
text.setTextFormat(new TextFormat("Tahoma", textSize*size, 0xFF0000));
|
||||
|
||||
canvas.addChild(text);
|
||||
canvas._numChildren++;
|
||||
}
|
||||
|
||||
// Ось Y
|
||||
if (p[8] >= camera.nearClipping) {
|
||||
gfx.lineStyle(lineThickness*size, 0x00FF00);
|
||||
gfx.moveTo(d[0], d[1]);
|
||||
gfx.lineTo(d[4], d[5]);
|
||||
|
||||
text = new TextField();
|
||||
text.autoSize = TextFieldAutoSize.LEFT;
|
||||
text.selectable = false;
|
||||
text.x = d[4];
|
||||
text.y = d[5];
|
||||
text.text = "Y";
|
||||
text.setTextFormat(new TextFormat("Tahoma", textSize*size, 0x00FF00));
|
||||
|
||||
canvas.addChild(text);
|
||||
canvas._numChildren++;
|
||||
}
|
||||
|
||||
// Ось Z
|
||||
if (p[11] >= camera.nearClipping) {
|
||||
gfx.lineStyle(lineThickness*size, 0x0000FF);
|
||||
gfx.moveTo(d[0], d[1]);
|
||||
gfx.lineTo(d[6], d[7]);
|
||||
|
||||
text = new TextField();
|
||||
text.autoSize = TextFieldAutoSize.LEFT;
|
||||
text.selectable = false;
|
||||
text.x = d[6];
|
||||
text.y = d[7];
|
||||
text.text = "Z";
|
||||
text.setTextFormat(new TextFormat("Tahoma", textSize*size, 0x0000FF));
|
||||
|
||||
canvas.addChild(text);
|
||||
canvas._numChildren++;
|
||||
}
|
||||
|
||||
// Начало координат
|
||||
gfx.lineStyle();
|
||||
gfx.beginFill(0xFFFFFF);
|
||||
gfx.drawCircle(d[0], d[1], dotRadius*size);
|
||||
|
||||
//debugDrawBoundRaduis(camera, object, canvas);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
57
Alternativa3D7/7.0/alternativa/engine3d/objects/Bone.as
Normal file
57
Alternativa3D7/7.0/alternativa/engine3d/objects/Bone.as
Normal file
@@ -0,0 +1,57 @@
|
||||
package alternativa.engine3d.objects {
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class Bone extends Mesh {
|
||||
|
||||
public var localTransform:Vector.<Vector3D>;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var localMatrix:Matrix3D;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var length:Number;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var distance:Number;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _numChildren:uint = 0;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var children:Vector.<Bone> = new Vector.<Bone>();
|
||||
|
||||
public function Bone(length:Number, distance:Number) {
|
||||
this.length = length;
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
public function addChild(child:Bone):void {
|
||||
children[_numChildren++] = child;
|
||||
child.localTransform = child.matrix.decompose();
|
||||
child.localMatrix = new Matrix3D();
|
||||
}
|
||||
|
||||
public function calculateMatrix():void {
|
||||
for (var i:int = 0; i < _numChildren; i++) {
|
||||
var child:Bone = children[i];
|
||||
child.matrix.identity();
|
||||
child.matrix.prepend(matrix);
|
||||
child.localMatrix.recompose(child.localTransform);
|
||||
child.matrix.prepend(child.localMatrix);
|
||||
child.calculateMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
74
Alternativa3D7/7.0/alternativa/engine3d/objects/LOD.as
Normal file
74
Alternativa3D7/7.0/alternativa/engine3d/objects/LOD.as
Normal file
@@ -0,0 +1,74 @@
|
||||
package alternativa.engine3d.objects {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Geometry;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.geom.Matrix3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Объект, имеющий набор объектов с разной детализацией.
|
||||
* При отрисовке, он выбирает в зависимости от расстояния от камеры
|
||||
* объект с нужной детализацией и отрисовывает его вместо себя.
|
||||
* Это позволяет получить лучший визуальный результат и большую производительность.
|
||||
*/
|
||||
public class LOD extends Object3D {
|
||||
|
||||
/**
|
||||
* Объекты с разной детализацией
|
||||
*/
|
||||
public var lodObjects:Vector.<Object3D>;
|
||||
/**
|
||||
* Расстояния до камеры соответствующие объектам с разной детализацией
|
||||
*/
|
||||
public var lodDistances:Vector.<Number>;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function getLODObject(object:Object3D):Object3D {
|
||||
var cameraDistance:Number = object.cameraMatrix.position.length;
|
||||
// Поиск ближайшего лода
|
||||
var min:Number = Infinity;
|
||||
var length:uint = lodObjects.length;
|
||||
var lod:Object3D;
|
||||
for (var i:int = 0; i < length; i++) {
|
||||
var d:Number = Math.abs(cameraDistance - lodDistances[i]);
|
||||
if (d < min) {
|
||||
min = d;
|
||||
lod = lodObjects[i];
|
||||
}
|
||||
}
|
||||
return lod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
getLODObject(object).draw(camera, object, parentCanvas);
|
||||
}
|
||||
|
||||
override alternativa3d function debug(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
getLODObject(object).debug(camera, object, parentCanvas);
|
||||
}
|
||||
|
||||
override alternativa3d function getGeometry(camera:Camera3D, object:Object3D):Geometry {
|
||||
return getLODObject(object).getGeometry(camera, object);
|
||||
}
|
||||
|
||||
override public function get boundBox():BoundBox {
|
||||
return (lodObjects[0] as Object3D).boundBox;
|
||||
}
|
||||
|
||||
override public function calculateBoundBox(matrix:Matrix3D = null, boundBox:BoundBox = null):BoundBox {
|
||||
return (lodObjects[0] as Object3D).calculateBoundBox(matrix, boundBox);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
3194
Alternativa3D7/7.0/alternativa/engine3d/objects/Mesh.as
Normal file
3194
Alternativa3D7/7.0/alternativa/engine3d/objects/Mesh.as
Normal file
File diff suppressed because it is too large
Load Diff
110
Alternativa3D7/7.0/alternativa/engine3d/objects/MeshReference.as
Normal file
110
Alternativa3D7/7.0/alternativa/engine3d/objects/MeshReference.as
Normal file
@@ -0,0 +1,110 @@
|
||||
package alternativa.engine3d.objects {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Geometry;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.display.BitmapData;
|
||||
import alternativa.engine3d.core.MipMap;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class MeshReference extends Object3D {
|
||||
|
||||
public var referenceMesh:Mesh;
|
||||
|
||||
public var sorting:int;
|
||||
|
||||
public var texture:BitmapData;
|
||||
|
||||
public var mipMapping:int;
|
||||
|
||||
public var mipMap:MipMap;
|
||||
|
||||
public function MeshReference(referenceMesh:Mesh = null, sorting:int = 0, texture:BitmapData = null, mipMapping:int = 0, mipMap:MipMap = null) {
|
||||
this.referenceMesh = referenceMesh;
|
||||
this.sorting = sorting;
|
||||
this.texture = texture;
|
||||
this.mipMapping = mipMapping;
|
||||
this.mipMap = mipMap;
|
||||
}
|
||||
|
||||
override alternativa3d function get canDraw():Boolean {
|
||||
return (texture != null || mipMap != null) && referenceMesh.numFaces > 0;
|
||||
}
|
||||
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
// Сохранение параметров
|
||||
var meshSorting:int = referenceMesh.sorting;
|
||||
var meshTexture:BitmapData = referenceMesh.texture;
|
||||
var meshMipMapping:int = referenceMesh.mipMapping;
|
||||
var meshMipMap:MipMap = referenceMesh.mipMap;
|
||||
// Назначение своих
|
||||
referenceMesh.sorting = sorting;
|
||||
referenceMesh.texture = texture;
|
||||
referenceMesh.mipMapping = mipMapping;
|
||||
referenceMesh.mipMap = mipMap;
|
||||
// Отрисовка
|
||||
referenceMesh.draw(camera, object, parentCanvas);
|
||||
// Возврат параметров
|
||||
referenceMesh.sorting = meshSorting;
|
||||
referenceMesh.texture = meshTexture;
|
||||
referenceMesh.mipMapping = meshMipMapping;
|
||||
referenceMesh.mipMap = meshMipMap;
|
||||
}
|
||||
|
||||
override alternativa3d function debug(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
// Сохранение параметров
|
||||
var meshSorting:int = referenceMesh.sorting;
|
||||
var meshTexture:BitmapData = referenceMesh.texture;
|
||||
var meshMipMapping:int = referenceMesh.mipMapping;
|
||||
var meshMipMap:MipMap = referenceMesh.mipMap;
|
||||
// Назначение своих
|
||||
referenceMesh.sorting = sorting;
|
||||
referenceMesh.texture = texture;
|
||||
referenceMesh.mipMapping = mipMapping;
|
||||
referenceMesh.mipMap = mipMap;
|
||||
// Отрисовка
|
||||
referenceMesh.debug(camera, object, parentCanvas);
|
||||
// Возврат параметров
|
||||
referenceMesh.sorting = meshSorting;
|
||||
referenceMesh.texture = meshTexture;
|
||||
referenceMesh.mipMapping = meshMipMapping;
|
||||
referenceMesh.mipMap = meshMipMap;
|
||||
}
|
||||
|
||||
override alternativa3d function getGeometry(camera:Camera3D, object:Object3D):Geometry {
|
||||
// Сохранение параметров
|
||||
var meshSorting:int = referenceMesh.sorting;
|
||||
var meshTexture:BitmapData = referenceMesh.texture;
|
||||
var meshMipMapping:int = referenceMesh.mipMapping;
|
||||
var meshMipMap:MipMap = referenceMesh.mipMap;
|
||||
// Назначение своих
|
||||
referenceMesh.sorting = sorting;
|
||||
referenceMesh.texture = texture;
|
||||
referenceMesh.mipMapping = mipMapping;
|
||||
referenceMesh.mipMap = mipMap;
|
||||
// Получение геометрии
|
||||
var geometry:Geometry = referenceMesh.getGeometry(camera, object);
|
||||
// Возврат параметров
|
||||
referenceMesh.sorting = meshSorting;
|
||||
referenceMesh.texture = meshTexture;
|
||||
referenceMesh.mipMapping = meshMipMapping;
|
||||
referenceMesh.mipMap = meshMipMap;
|
||||
return geometry;
|
||||
}
|
||||
|
||||
override public function get boundBox():BoundBox {
|
||||
return referenceMesh.boundBox;
|
||||
}
|
||||
|
||||
override public function calculateBoundBox(matrix:Matrix3D = null, boundBox:BoundBox = null):BoundBox {
|
||||
return referenceMesh.calculateBoundBox(matrix, boundBox);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
421
Alternativa3D7/7.0/alternativa/engine3d/objects/Occluder.as
Normal file
421
Alternativa3D7/7.0/alternativa/engine3d/objects/Occluder.as
Normal file
@@ -0,0 +1,421 @@
|
||||
package alternativa.engine3d.objects {
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Debug;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Utils3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Полигональный объект-перекрытие.
|
||||
* Объекты, которые он перекрывает от видимости камеры, исключаются из отрисовки.
|
||||
* Сам окклюдер не отрисовывается.
|
||||
* Должен быть конвексным
|
||||
*/
|
||||
public class Occluder extends Object3D {
|
||||
/**
|
||||
* Режим представления полигонов.
|
||||
* Если false, в indices записаны треугольники (тройки индексов).
|
||||
* Если true, в indices записаны многоугольники в виде: количество вершин грани, индексы вершин
|
||||
*/
|
||||
public var poly:Boolean = false;
|
||||
public var vertices:Vector.<Number>;
|
||||
public var edges:Vector.<int>; // Два индекса - вершины, два - грани
|
||||
public var indices:Vector.<int>;
|
||||
public var normals:Vector.<Number>;
|
||||
|
||||
// Отношение площади перекрытия к площади вьюпорта (0 - 1)
|
||||
/**
|
||||
* Минимальное отношение площади перекрытия окклюдером вьюпорта к площади вьюпорта (от 0 до 1)
|
||||
* Если окклюдер перекрывает больше, он помещается в очередь и учитывается
|
||||
* при дальнейшей отрисовке в пределах кадра, иначе игнорируется
|
||||
*/
|
||||
public var minSize:Number = 0;
|
||||
|
||||
private const cameraVertices:Vector.<Number> = new Vector.<Number>();
|
||||
private const visibilityMap:Vector.<Boolean> = new Vector.<Boolean>;
|
||||
|
||||
/**
|
||||
* Коприрование геометрии меша
|
||||
* @param mesh Объект копирования
|
||||
* Меш, геометрия которого копируется, обязан быть конвексным, иначе окклюдер будет некорректно работать
|
||||
*/
|
||||
public function copyFrom(mesh:Mesh):void {
|
||||
poly = mesh.poly;
|
||||
vertices = mesh.vertices;
|
||||
indices = mesh.indices;
|
||||
normals = mesh.normals;
|
||||
|
||||
matrix.identity();
|
||||
matrix.prepend(mesh.matrix);
|
||||
if (_boundBox != null) {
|
||||
_boundBox.copyFrom(mesh._boundBox);
|
||||
} else {
|
||||
_boundBox = mesh._boundBox;
|
||||
}
|
||||
}
|
||||
|
||||
override public function calculateBoundBox(matrix:Matrix3D = null, boundBox:BoundBox = null):BoundBox {
|
||||
var v:Vector.<Number> = vertices;
|
||||
// Если указана матрица трансформации, переводим
|
||||
if (matrix != null) {
|
||||
matrix.transformVectors(vertices, cameraVertices);
|
||||
v = cameraVertices;
|
||||
}
|
||||
// Если указан баунд-бокс
|
||||
if (boundBox != null) {
|
||||
boundBox.infinity();
|
||||
} else {
|
||||
boundBox = new BoundBox();
|
||||
}
|
||||
// Ищем баунд-бокс
|
||||
for (var i:int = 0, length:int = vertices.length; i < length;) {
|
||||
boundBox.addPoint(v[i++], v[i++], v[i++]);
|
||||
}
|
||||
return boundBox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Расчёт рёбер по имеющимся вершинам и граням
|
||||
*/
|
||||
public function calculateEdges():void {
|
||||
// Подготавливаем массив рёбер
|
||||
if (edges == null) edges = new Vector.<int>();
|
||||
|
||||
// Собираем рёбра
|
||||
for (var i:int = 0, j:int = 0, n:int = 0, k:int = 0, a:int, b:int, length:int = indices.length; i < length;) {
|
||||
if (i == k) {
|
||||
k = poly ? (indices[i++] + i) : (i + 3);
|
||||
a = indices[int(k - 1)];
|
||||
}
|
||||
b = indices[i];
|
||||
edges[j++] = a;
|
||||
edges[j++] = b;
|
||||
edges[j++] = n;
|
||||
edges[j++] = -1;
|
||||
if (++i == k) n++; else a = b;
|
||||
}
|
||||
edges.length = j;
|
||||
|
||||
// Убираем дубли
|
||||
length = j, i = 0; k = 0;
|
||||
var ac:int, bc:int;
|
||||
while (i < length) {
|
||||
if ((a = edges[i++]) >= 0) {
|
||||
b = edges[i++];
|
||||
edges[k++] = a;
|
||||
edges[k++] = b;
|
||||
edges[k++] = edges[i++];
|
||||
j = ++i;
|
||||
while (j < length) {
|
||||
ac = edges[j++];
|
||||
bc = edges[j++];
|
||||
if (ac == a && bc == b || ac == b && bc == a) {
|
||||
edges[int(j - 2)] = -1;
|
||||
edges[k] = edges[j];
|
||||
break;
|
||||
}
|
||||
j += 2;
|
||||
}
|
||||
k++;
|
||||
} else i += 3;
|
||||
}
|
||||
edges.length = k;
|
||||
}
|
||||
|
||||
static private const inverseCameraMatrix:Matrix3D = new Matrix3D();
|
||||
static private const center:Vector.<Number> = new Vector.<Number>(3, true);
|
||||
private var cameraX:Number;
|
||||
private var cameraY:Number;
|
||||
private var cameraZ:Number;
|
||||
static private const projectedEdges:Vector.<Number> = new Vector.<Number>();
|
||||
static private const uvts:Vector.<Number> = new Vector.<Number>();
|
||||
static private const viewEdges:Vector.<Number> = new Vector.<Number>();
|
||||
static private const debugEdges:Vector.<Number> = new Vector.<Number>();
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
// Перевод в координаты камеры
|
||||
object.cameraMatrix.transformVectors(vertices, cameraVertices);
|
||||
// Определение центра камеры в объекте
|
||||
inverseCameraMatrix.identity();
|
||||
inverseCameraMatrix.prepend(object.cameraMatrix);
|
||||
inverseCameraMatrix.invert();
|
||||
center[0] = center[1] = center[2] = 0;
|
||||
inverseCameraMatrix.transformVectors(center, center);
|
||||
cameraX = center[0], cameraY = center[1], cameraZ = center[2];
|
||||
// Расчёт карты видимости граней
|
||||
for (var i:int = 0, n:int = 0, normalsLength:int = normals.length >> 2, cameraInside:Boolean = true, infront:Boolean; i < normalsLength;) visibilityMap[i++] = infront = normals[n++]*cameraX + normals[n++]*cameraY + normals[n++]*cameraZ > normals[n++], cameraInside &&= !infront;
|
||||
// Если камера внутри окклюдера
|
||||
if (cameraInside) return;
|
||||
// Подготовка окклюдера в камере
|
||||
var occludeAll:Boolean = true, culling:int = object.culling, direction:Boolean, ax:Number, ay:Number, az:Number, bx:Number, by:Number, bz:Number, t:Number;
|
||||
var planeOccluder:Vector.<Number>, edgeOccluder:Vector.<Number>, planeOccluderLength:int = 0, edgeOccluderLength:int = 0, viewEdgesLength:int = 0;
|
||||
if (camera.occlusionPlanes.length > camera.numOccluders) {
|
||||
planeOccluder = camera.occlusionPlanes[camera.numOccluders];
|
||||
edgeOccluder = camera.occlusionEdges[camera.numOccluders];
|
||||
} else {
|
||||
planeOccluder = camera.occlusionPlanes[camera.numOccluders] = new Vector.<Number>();
|
||||
edgeOccluder = camera.occlusionEdges[camera.numOccluders] = new Vector.<Number>();
|
||||
}
|
||||
for (i = edges.length - 1; i > 0;) {
|
||||
if ((direction = visibilityMap[edges[i--]]) != visibilityMap[edges[i--]]) {
|
||||
// Определение порядка вершин (против часовой)
|
||||
if (direction) {
|
||||
ax = cameraVertices[n = int(edges[i--]*3)], ay = cameraVertices[++n], az = cameraVertices[++n], bx = cameraVertices[n = int(edges[i--]*3)], by = cameraVertices[++n], bz = cameraVertices[++n];
|
||||
} else {
|
||||
bx = cameraVertices[n = int(edges[i--]*3)], by = cameraVertices[++n], bz = cameraVertices[++n], ax = cameraVertices[n = int(edges[i--]*3)], ay = cameraVertices[++n], az = cameraVertices[++n];
|
||||
}
|
||||
// Клиппинг
|
||||
if (culling > 0) {
|
||||
if (az <= -ax && bz <= -bx) {
|
||||
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
|
||||
continue;
|
||||
} else if (bz > -bx && az <= -ax) {
|
||||
t = (ax + az)/(ax + az - bx - bz), ax = ax + (bx - ax)*t, ay = ay + (by - ay)*t, az = az + (bz - az)*t;
|
||||
} else if (bz <= -bx && az > -ax) {
|
||||
t = (ax + az)/(ax + az - bx - bz), bx = ax + (bx - ax)*t, by = ay + (by - ay)*t, bz = az + (bz - az)*t;
|
||||
}
|
||||
if (az <= ax && bz <= bx) {
|
||||
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
|
||||
continue;
|
||||
} else if (bz > bx && az <= ax) {
|
||||
t = (az - ax)/(az - ax + bx - bz), ax = ax + (bx - ax)*t, ay = ay + (by - ay)*t, az = az + (bz - az)*t;
|
||||
} else if (bz <= bx && az > ax) {
|
||||
t = (az - ax)/(az - ax + bx - bz), bx = ax + (bx - ax)*t, by = ay + (by - ay)*t, bz = az + (bz - az)*t;
|
||||
}
|
||||
if (az <= -ay && bz <= -by) {
|
||||
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
|
||||
continue;
|
||||
} else if (bz > -by && az <= -ay) {
|
||||
t = (ay + az)/(ay + az - by - bz), ax = ax + (bx - ax)*t, ay = ay + (by - ay)*t, az = az + (bz - az)*t;
|
||||
} else if (bz <= -by && az > -ay) {
|
||||
t = (ay + az)/(ay + az - by - bz), bx = ax + (bx - ax)*t, by = ay + (by - ay)*t, bz = az + (bz - az)*t;
|
||||
}
|
||||
if (az <= ay && bz <= by) {
|
||||
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
|
||||
continue;
|
||||
} else if (bz > by && az <= ay) {
|
||||
t = (az - ay)/(az - ay + by - bz), ax = ax + (bx - ax)*t, ay = ay + (by - ay)*t, az = az + (bz - az)*t;
|
||||
} else if (bz <= by && az > ay) {
|
||||
t = (az - ay)/(az - ay + by - bz), bx = ax + (bx - ax)*t, by = ay + (by - ay)*t, bz = az + (bz - az)*t;
|
||||
}
|
||||
occludeAll = false;
|
||||
}
|
||||
// Расчёт нормали плоскости отсечения
|
||||
planeOccluder[planeOccluderLength++] = bz*ay - by*az, planeOccluder[planeOccluderLength++] = bx*az - bz*ax, planeOccluder[planeOccluderLength++] = by*ax - bx*ay;
|
||||
// Сохранение рёбер
|
||||
edgeOccluder[edgeOccluderLength++] = ax, edgeOccluder[edgeOccluderLength++] = ay, edgeOccluder[edgeOccluderLength++] = az, edgeOccluder[edgeOccluderLength++] = bx, edgeOccluder[edgeOccluderLength++] = by, edgeOccluder[edgeOccluderLength++] = bz;
|
||||
} else i -= 2;
|
||||
}
|
||||
if (planeOccluderLength > 0) {
|
||||
// Проверка размера на экране
|
||||
if (minSize > 0) {
|
||||
// Проецирование рёбер контура
|
||||
var projectedEdgesLength:int = projectedEdges.length = ((edgeOccluder.length = edgeOccluderLength)/3) << 1;
|
||||
Utils3D.projectVectors(camera.projectionMatrix, edgeOccluder, projectedEdges, uvts);
|
||||
// Клиппинг рамки вьюпорта
|
||||
if (culling > 0) {
|
||||
if (culling & 4) viewEdges[viewEdgesLength++] = -camera.viewSizeX, viewEdges[viewEdgesLength++] = -camera.viewSizeY, viewEdges[viewEdgesLength++] = -camera.viewSizeX, viewEdges[viewEdgesLength++] = camera.viewSizeY;
|
||||
if (culling & 8) viewEdges[viewEdgesLength++] = camera.viewSizeX, viewEdges[viewEdgesLength++] = camera.viewSizeY, viewEdges[viewEdgesLength++] = camera.viewSizeX, viewEdges[viewEdgesLength++] = -camera.viewSizeY;
|
||||
if (culling & 16) viewEdges[viewEdgesLength++] = camera.viewSizeX, viewEdges[viewEdgesLength++] = -camera.viewSizeY, viewEdges[viewEdgesLength++] = -camera.viewSizeX, viewEdges[viewEdgesLength++] = -camera.viewSizeY;
|
||||
if (culling & 32) viewEdges[viewEdgesLength++] = -camera.viewSizeX, viewEdges[viewEdgesLength++] = camera.viewSizeY, viewEdges[viewEdgesLength++] = camera.viewSizeX, viewEdges[viewEdgesLength++] = camera.viewSizeY;
|
||||
if (viewEdgesLength > 0) {
|
||||
for (i = 0; i < projectedEdgesLength;) {
|
||||
ax = projectedEdges[i++], ay = projectedEdges[i++], bx = projectedEdges[i++], by = projectedEdges[i++];
|
||||
var nx:Number = ay - by, ny:Number = bx - ax, no:Number = nx*ax + ny*ay;
|
||||
for (var j:int = 0, j2:int = 0; j < viewEdgesLength;) {
|
||||
ax = viewEdges[j++], ay = viewEdges[j++], bx = viewEdges[j++], by = viewEdges[j++], az = ax*nx + ay*ny - no, bz = bx*nx + by*ny - no;
|
||||
if (az < 0 || bz < 0) {
|
||||
if (az >= 0 && bz < 0) {
|
||||
t = az/(az - bz), viewEdges[j2++] = ax + (bx - ax)*t, viewEdges[j2++] = ay + (by - ay)*t, viewEdges[j2++] = bx, viewEdges[j2++] = by;
|
||||
} else if (az < 0 && bz >= 0) {
|
||||
t = az/(az - bz), viewEdges[j2++] = ax, viewEdges[j2++] = ay, viewEdges[j2++] = ax + (bx - ax)*t, viewEdges[j2++] = ay + (by - ay)*t;
|
||||
} else {
|
||||
viewEdges[j2++] = ax, viewEdges[j2++] = ay, viewEdges[j2++] = bx, viewEdges[j2++] = by;
|
||||
}
|
||||
}
|
||||
}
|
||||
viewEdgesLength = j2;
|
||||
if (viewEdgesLength == 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Нахождение площади перекрытия
|
||||
var square:Number = 0;
|
||||
for (i = 0, az = projectedEdges[i++], bz = projectedEdges[i++], i += 2; i < projectedEdgesLength;) ax = projectedEdges[i++] - az, ay = projectedEdges[i++] - bz, bx = projectedEdges[i++] - az, by = projectedEdges[i++] - bz, square += bx*ay - by*ax;
|
||||
for (i = 0; i < viewEdgesLength;) ax = viewEdges[i++] - az, ay = viewEdges[i++] - bz, bx = viewEdges[i++] - az, by = viewEdges[i++] - bz, square += bx*ay - by*ax;
|
||||
if (square/(camera.viewSizeX*camera.viewSizeY*8) < minSize) return;
|
||||
}
|
||||
// Добавление окклюдера
|
||||
camera.numOccluders++;
|
||||
planeOccluder.length = planeOccluderLength;
|
||||
edgeOccluder.length = edgeOccluderLength;
|
||||
} else {
|
||||
if (occludeAll) {
|
||||
camera.numOccluders = 0, camera.occludedAll = true;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override alternativa3d function debug(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
var inDebug:int = camera.checkInDebug(this);
|
||||
if (inDebug == 0) return;
|
||||
var canvas:Canvas = parentCanvas.getChildCanvas(true, false);
|
||||
|
||||
// Рёбра
|
||||
if (inDebug & Debug.EDGES) {
|
||||
object.cameraMatrix.transformVectors(vertices, cameraVertices);
|
||||
inverseCameraMatrix.identity();
|
||||
inverseCameraMatrix.prepend(object.cameraMatrix);
|
||||
inverseCameraMatrix.invert();
|
||||
center[0] = center[1] = center[2] = 0;
|
||||
inverseCameraMatrix.transformVectors(center, center);
|
||||
cameraX = center[0], cameraY = center[1], cameraZ = center[2];
|
||||
// Расчёт карты видимости граней
|
||||
for (var i:int = 0, n:int = 0, normalsLength:int = normals.length >> 2, cameraInside:Boolean = true, infront:Boolean; i < normalsLength;) visibilityMap[i++] = infront = normals[n++]*cameraX + normals[n++]*cameraY + normals[n++]*cameraZ > normals[n++], cameraInside &&= !infront;
|
||||
// Если камера внутри окклюдера
|
||||
if (!cameraInside) {
|
||||
// Подготовка окклюдера в камере
|
||||
var occludeAll:Boolean = true, culling:int = object.culling, direction:Boolean, ax:Number, ay:Number, az:Number, bx:Number, by:Number, bz:Number, t:Number;
|
||||
var debugEdgesLength:int = 0, viewEdgesLength:int = 0;
|
||||
for (i = edges.length - 1; i > 0;) {
|
||||
if ((direction = visibilityMap[edges[i--]]) != visibilityMap[edges[i--]]) {
|
||||
// Определение порядка вершин (против часовой)
|
||||
if (direction) {
|
||||
ax = cameraVertices[n = int(edges[i--]*3)], ay = cameraVertices[++n], az = cameraVertices[++n], bx = cameraVertices[n = int(edges[i--]*3)], by = cameraVertices[++n], bz = cameraVertices[++n];
|
||||
} else {
|
||||
bx = cameraVertices[n = int(edges[i--]*3)], by = cameraVertices[++n], bz = cameraVertices[++n], ax = cameraVertices[n = int(edges[i--]*3)], ay = cameraVertices[++n], az = cameraVertices[++n];
|
||||
}
|
||||
// Клиппинг
|
||||
if (culling > 0) {
|
||||
if (az <= -ax && bz <= -bx) {
|
||||
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
|
||||
continue;
|
||||
} else if (bz > -bx && az <= -ax) {
|
||||
t = (ax + az)/(ax + az - bx - bz), ax = ax + (bx - ax)*t, ay = ay + (by - ay)*t, az = az + (bz - az)*t;
|
||||
} else if (bz <= -bx && az > -ax) {
|
||||
t = (ax + az)/(ax + az - bx - bz), bx = ax + (bx - ax)*t, by = ay + (by - ay)*t, bz = az + (bz - az)*t;
|
||||
}
|
||||
if (az <= ax && bz <= bx) {
|
||||
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
|
||||
continue;
|
||||
} else if (bz > bx && az <= ax) {
|
||||
t = (az - ax)/(az - ax + bx - bz), ax = ax + (bx - ax)*t, ay = ay + (by - ay)*t, az = az + (bz - az)*t;
|
||||
} else if (bz <= bx && az > ax) {
|
||||
t = (az - ax)/(az - ax + bx - bz), bx = ax + (bx - ax)*t, by = ay + (by - ay)*t, bz = az + (bz - az)*t;
|
||||
}
|
||||
if (az <= -ay && bz <= -by) {
|
||||
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
|
||||
continue;
|
||||
} else if (bz > -by && az <= -ay) {
|
||||
t = (ay + az)/(ay + az - by - bz), ax = ax + (bx - ax)*t, ay = ay + (by - ay)*t, az = az + (bz - az)*t;
|
||||
} else if (bz <= -by && az > -ay) {
|
||||
t = (ay + az)/(ay + az - by - bz), bx = ax + (bx - ax)*t, by = ay + (by - ay)*t, bz = az + (bz - az)*t;
|
||||
}
|
||||
if (az <= ay && bz <= by) {
|
||||
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
|
||||
continue;
|
||||
} else if (bz > by && az <= ay) {
|
||||
t = (az - ay)/(az - ay + by - bz), ax = ax + (bx - ax)*t, ay = ay + (by - ay)*t, az = az + (bz - az)*t;
|
||||
} else if (bz <= by && az > ay) {
|
||||
t = (az - ay)/(az - ay + by - bz), bx = ax + (bx - ax)*t, by = ay + (by - ay)*t, bz = az + (bz - az)*t;
|
||||
}
|
||||
occludeAll = false;
|
||||
}
|
||||
debugEdges[debugEdgesLength++] = ax;
|
||||
debugEdges[debugEdgesLength++] = ay;
|
||||
debugEdges[debugEdgesLength++] = az;
|
||||
debugEdges[debugEdgesLength++] = bx;
|
||||
debugEdges[debugEdgesLength++] = by;
|
||||
debugEdges[debugEdgesLength++] = bz;
|
||||
} else i -= 2;
|
||||
}
|
||||
if (debugEdgesLength > 0) {
|
||||
// Проецирование рёбер контура
|
||||
var projectedEdgesLength:int = projectedEdges.length = ((debugEdges.length = debugEdgesLength)/3) << 1;
|
||||
Utils3D.projectVectors(camera.projectionMatrix, debugEdges, projectedEdges, uvts);
|
||||
// Проверка размера на экране
|
||||
var square:Number = Number.MAX_VALUE;
|
||||
if (minSize > 0) {
|
||||
// Клиппинг рамки вьюпорта
|
||||
if (culling > 0) {
|
||||
if (culling & 4) viewEdges[viewEdgesLength++] = -camera.viewSizeX, viewEdges[viewEdgesLength++] = -camera.viewSizeY, viewEdges[viewEdgesLength++] = -camera.viewSizeX, viewEdges[viewEdgesLength++] = camera.viewSizeY;
|
||||
if (culling & 8) viewEdges[viewEdgesLength++] = camera.viewSizeX, viewEdges[viewEdgesLength++] = camera.viewSizeY, viewEdges[viewEdgesLength++] = camera.viewSizeX, viewEdges[viewEdgesLength++] = -camera.viewSizeY;
|
||||
if (culling & 16) viewEdges[viewEdgesLength++] = camera.viewSizeX, viewEdges[viewEdgesLength++] = -camera.viewSizeY, viewEdges[viewEdgesLength++] = -camera.viewSizeX, viewEdges[viewEdgesLength++] = -camera.viewSizeY;
|
||||
if (culling & 32) viewEdges[viewEdgesLength++] = -camera.viewSizeX, viewEdges[viewEdgesLength++] = camera.viewSizeY, viewEdges[viewEdgesLength++] = camera.viewSizeX, viewEdges[viewEdgesLength++] = camera.viewSizeY;
|
||||
if (viewEdgesLength > 0) {
|
||||
for (i = 0; i < projectedEdgesLength;) {
|
||||
ax = projectedEdges[i++], ay = projectedEdges[i++], bx = projectedEdges[i++], by = projectedEdges[i++];
|
||||
var nx:Number = ay - by, ny:Number = bx - ax, no:Number = nx*ax + ny*ay;
|
||||
for (var j:int = 0, j2:int = 0; j < viewEdgesLength;) {
|
||||
ax = viewEdges[j++], ay = viewEdges[j++], bx = viewEdges[j++], by = viewEdges[j++], az = ax*nx + ay*ny - no, bz = bx*nx + by*ny - no;
|
||||
if (az < 0 || bz < 0) {
|
||||
if (az >= 0 && bz < 0) {
|
||||
t = az/(az - bz), viewEdges[j2++] = ax + (bx - ax)*t, viewEdges[j2++] = ay + (by - ay)*t, viewEdges[j2++] = bx, viewEdges[j2++] = by;
|
||||
} else if (az < 0 && bz >= 0) {
|
||||
t = az/(az - bz), viewEdges[j2++] = ax, viewEdges[j2++] = ay, viewEdges[j2++] = ax + (bx - ax)*t, viewEdges[j2++] = ay + (by - ay)*t;
|
||||
} else {
|
||||
viewEdges[j2++] = ax, viewEdges[j2++] = ay, viewEdges[j2++] = bx, viewEdges[j2++] = by;
|
||||
}
|
||||
}
|
||||
}
|
||||
viewEdgesLength = j2;
|
||||
if (viewEdgesLength == 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Нахождение площади перекрытия
|
||||
square = 0;
|
||||
for (i = 0, az = projectedEdges[i++], bz = projectedEdges[i++], i += 2; i < projectedEdgesLength;) ax = projectedEdges[i++] - az, ay = projectedEdges[i++] - bz, bx = projectedEdges[i++] - az, by = projectedEdges[i++] - bz, square += bx*ay - by*ax;
|
||||
for (i = 0; i < viewEdgesLength;) ax = viewEdges[i++] - az, ay = viewEdges[i++] - bz, bx = viewEdges[i++] - az, by = viewEdges[i++] - bz, square += bx*ay - by*ax;
|
||||
}
|
||||
if (canvas == null) canvas = parentCanvas.getChildCanvas(true, false);
|
||||
var color:int, thickness:Number;
|
||||
if (square/(camera.viewSizeX*camera.viewSizeY*8) >= minSize) {
|
||||
color = 0x0000FF, thickness = 3;
|
||||
} else {
|
||||
color = 0x0077AA, thickness = 1;
|
||||
}
|
||||
for (i = 0; i < projectedEdges.length;) {
|
||||
ax = projectedEdges[i++], ay = projectedEdges[i++], bx = projectedEdges[i++], by = projectedEdges[i++];
|
||||
canvas.gfx.moveTo(ax, ay);
|
||||
canvas.gfx.lineStyle(thickness, color);
|
||||
canvas.gfx.lineTo(ax + (bx - ax)*0.8, ay + (by - ay)*0.8);
|
||||
canvas.gfx.lineStyle(thickness, 0xFF0000);
|
||||
canvas.gfx.lineTo(bx, by);
|
||||
}
|
||||
for (i = 0; i < viewEdgesLength;) {
|
||||
canvas.gfx.moveTo(viewEdges[i++], viewEdges[i++]);
|
||||
canvas.gfx.lineTo(viewEdges[i++], viewEdges[i++]);
|
||||
}
|
||||
} else {
|
||||
if (occludeAll) {
|
||||
if (canvas == null) canvas = parentCanvas.getChildCanvas(true, false);
|
||||
canvas.gfx.lineStyle(6, 0xFF0000);
|
||||
canvas.gfx.moveTo(-camera.viewSizeX, -camera.viewSizeY);
|
||||
canvas.gfx.lineTo(-camera.viewSizeX, camera.viewSizeY);
|
||||
canvas.gfx.lineTo(camera.viewSizeX, camera.viewSizeY);
|
||||
canvas.gfx.lineTo(camera.viewSizeX, -camera.viewSizeY);
|
||||
canvas.gfx.lineTo(-camera.viewSizeX, -camera.viewSizeY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Оси, центры, имена, баунды
|
||||
if (inDebug & Debug.AXES) object.drawAxes(camera, canvas);
|
||||
if (inDebug & Debug.CENTERS) object.drawCenter(camera, canvas);
|
||||
if (inDebug & Debug.NAMES) object.drawName(camera, canvas);
|
||||
if (inDebug & Debug.BOUNDS) object.drawBoundBox(camera, canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
56
Alternativa3D7/7.0/alternativa/engine3d/objects/Reference.as
Normal file
56
Alternativa3D7/7.0/alternativa/engine3d/objects/Reference.as
Normal file
@@ -0,0 +1,56 @@
|
||||
package alternativa.engine3d.objects {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Geometry;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.geom.Matrix3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Объект-ссылка.
|
||||
* Может ссылаться на любой трёхмерный объект, в том числе контейнер с любой вложенностью или Reference.
|
||||
* При отрисовке он отрисовывает вместо себя объект,
|
||||
* на который ссылается, подставляя только свою трансформацию, alpha, blendMode, colorTransform и filters.
|
||||
*/
|
||||
public class Reference extends Object3D {
|
||||
|
||||
/**
|
||||
* Объект, который подставляется при отрисовке вместо себя
|
||||
*/
|
||||
public var referenceObject:Object3D;
|
||||
|
||||
public function Reference(referenceObject:Object3D = null) {
|
||||
this.referenceObject = referenceObject;
|
||||
}
|
||||
|
||||
override alternativa3d function get canDraw():Boolean {
|
||||
return referenceObject.canDraw;
|
||||
}
|
||||
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
referenceObject.draw(camera, object, parentCanvas);
|
||||
}
|
||||
|
||||
override alternativa3d function debug(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
referenceObject.debug(camera, object, parentCanvas);
|
||||
}
|
||||
|
||||
override alternativa3d function getGeometry(camera:Camera3D, object:Object3D):Geometry {
|
||||
return referenceObject.getGeometry(camera, object);
|
||||
}
|
||||
|
||||
override public function get boundBox():BoundBox {
|
||||
return referenceObject.boundBox;
|
||||
}
|
||||
|
||||
override public function calculateBoundBox(matrix:Matrix3D = null, boundBox:BoundBox = null):BoundBox {
|
||||
return referenceObject.calculateBoundBox(matrix, boundBox);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
146
Alternativa3D7/7.0/alternativa/engine3d/objects/SkeletalMesh.as
Normal file
146
Alternativa3D7/7.0/alternativa/engine3d/objects/SkeletalMesh.as
Normal file
@@ -0,0 +1,146 @@
|
||||
package alternativa.engine3d.objects {
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class SkeletalMesh extends Mesh {
|
||||
|
||||
alternativa3d var _numBones:uint = 0;
|
||||
alternativa3d var bones:Vector.<Bone> = new Vector.<Bone>();
|
||||
private var weights:Vector.<Vector.<Number>>;
|
||||
private var originalVertices:Vector.<Number>;
|
||||
private var originalBonesMatrices:Vector.<Matrix3D>;
|
||||
private var boneVertices:Vector.<Number>;
|
||||
|
||||
public function initBones():void {
|
||||
// Инициализируем массив весов и сохраняем оригинальные матрицы костей
|
||||
originalBonesMatrices = new Vector.<Matrix3D>(_numBones, true);
|
||||
weights = new Vector.<Vector.<Number>>(_numBones, true);
|
||||
for (var j:int = 0; j < _numBones; j++) {
|
||||
originalBonesMatrices[j] = new Matrix3D();
|
||||
originalBonesMatrices[j].prepend(bones[j].matrix);
|
||||
originalBonesMatrices[j].invert();
|
||||
weights[j] = new Vector.<Number>(numVertices, true);
|
||||
}
|
||||
// Формируем вспомогательные массивы для вершин
|
||||
originalVertices = new Vector.<Number>(numVertices*3, true);
|
||||
boneVertices = new Vector.<Number>(numVertices*3, true);
|
||||
|
||||
// Обрабатываем вершины
|
||||
var v:Vector3D = new Vector3D();
|
||||
for (var i:int = 0; i < numVertices; i++) {
|
||||
var k:int = i*3;
|
||||
// Сохраняем оригинальные координаты вершин
|
||||
originalVertices[k] = vertices[k];
|
||||
originalVertices[k + 1] = vertices[k + 1];
|
||||
originalVertices[k + 2] = vertices[k + 2];
|
||||
|
||||
// Находим веса для каждой кости
|
||||
var sumWeight:Number = 0;
|
||||
for (j = 0; j < _numBones; j++) {
|
||||
// Находим расстояние от вершины до кости
|
||||
var b1:Vector3D = bones[j].matrix.transformVector(new Vector3D());
|
||||
var b2:Vector3D = bones[j].matrix.transformVector(new Vector3D(0, 0, bones[j].length));
|
||||
v.x = originalVertices[k];
|
||||
v.y = originalVertices[k + 1];
|
||||
v.z = originalVertices[k + 2];
|
||||
var w:Number = 1 - distanceToBone(b1, b2, v)/bones[j].distance;
|
||||
//trace(w);
|
||||
w = (w > 0) ? w : 0;
|
||||
weights[j][i] = w;
|
||||
sumWeight += w;
|
||||
}
|
||||
|
||||
// Нормализуем веса
|
||||
if (sumWeight > 0) {
|
||||
for (j = 0; j < _numBones; j++) {
|
||||
weights[j][i] /= sumWeight;
|
||||
}
|
||||
} else {
|
||||
// Если вершина не относится ни к какой кости, помечаем
|
||||
for (j = 0; j < _numBones; j++) {
|
||||
weights[j][i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private function distanceToBone(b1:Vector3D, b2:Vector3D, p:Vector3D):Number {
|
||||
var v:Vector3D = b2.subtract(b1);
|
||||
var w:Vector3D = p.subtract(b1);
|
||||
|
||||
var c1:Number = w.dotProduct(v);
|
||||
if ( c1 <= 0 )
|
||||
return Vector3D.distance(p, b1);
|
||||
|
||||
var c2:Number = v.dotProduct(v);
|
||||
if ( c2 <= c1 )
|
||||
return Vector3D.distance(p, b2);
|
||||
|
||||
v.scaleBy(c1 / c2);
|
||||
var Pb:Vector3D = b1.add(v);
|
||||
return Vector3D.distance(p, Pb);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private var m:Matrix3D = new Matrix3D();
|
||||
public function calculateBones():void {
|
||||
// Обнуление координат
|
||||
for (var i:int = 0; i < numVertices*3; i++) {
|
||||
vertices[i] = 0;
|
||||
}
|
||||
// Добавление трансформации через кости
|
||||
for (var j:int = 0; j < _numBones; j++) {
|
||||
m.identity();
|
||||
m.prepend(bones[j].matrix);
|
||||
m.prepend(originalBonesMatrices[j]);
|
||||
m.transformVectors(originalVertices, boneVertices);
|
||||
var boneWeights:Vector.<Number> = weights[j];
|
||||
for (i = 0; i < numVertices; i++) {
|
||||
var weight:Number = boneWeights[i];
|
||||
var k1:int = i*3;
|
||||
var k2:int = k1 + 1;
|
||||
var k3:int = k1 + 2;
|
||||
if (weight >= 0) {
|
||||
vertices[k1] += boneVertices[k1]*weight;
|
||||
vertices[k2] += boneVertices[k2]*weight;
|
||||
vertices[k3] += boneVertices[k3]*weight;
|
||||
} else {
|
||||
vertices[k1] = originalVertices[k1];
|
||||
vertices[k2] = originalVertices[k2];
|
||||
vertices[k3] = originalVertices[k3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function addBone(bone:Bone):void {
|
||||
bones[_numBones++] = bone;
|
||||
}
|
||||
/*
|
||||
public function removeChild(bone:Bone):void {
|
||||
var i:int = bones.indexOf(bone);
|
||||
if (i < 0) throw new ArgumentError();
|
||||
children.splice(i, 1);
|
||||
_numBones--;
|
||||
var len:uint = drawChildren.length;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (drawChildren[i] == bone) {
|
||||
drawChildren.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
bone._parent = null;
|
||||
bone.setStage(null);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
508
Alternativa3D7/7.0/alternativa/engine3d/objects/Sprite3D.as
Normal file
508
Alternativa3D7/7.0/alternativa/engine3d/objects/Sprite3D.as
Normal file
@@ -0,0 +1,508 @@
|
||||
package alternativa.engine3d.objects {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Debug;
|
||||
import alternativa.engine3d.core.Fragment;
|
||||
import alternativa.engine3d.core.Geometry;
|
||||
import alternativa.engine3d.core.MipMap;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.display.BitmapData;
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Matrix3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Плоский, всегда развёрнутый к камере трёхмерный объект
|
||||
*/
|
||||
public class Sprite3D extends Object3D {
|
||||
public var texture:BitmapData;
|
||||
public var smooth:Boolean = false;
|
||||
/**
|
||||
* Режим сортировки на случай конфликта
|
||||
* 0 - без сортировки
|
||||
* 1 - сортировка по средним Z
|
||||
* 2 - проход по предрасчитанному BSP. Для расчёта BSP нужен calculateBSP()
|
||||
* 3 - построение динамического BSP при отрисовке
|
||||
*/
|
||||
public var sorting:int = 0;
|
||||
/**
|
||||
* Применение мипмаппинга
|
||||
* 0 - без мипмаппинга
|
||||
* 1 - мипмаппинг по удалённости от камеры. Требуется установка свойства mipMap
|
||||
*/
|
||||
public var mipMapping:int = 0;
|
||||
public var mipMap:MipMap;
|
||||
/**
|
||||
* X точки привязки
|
||||
*/
|
||||
public var originX:Number = 0.5;
|
||||
/**
|
||||
* Y точки привязки
|
||||
*/
|
||||
public var originY:Number = 0.5;
|
||||
/**
|
||||
* Режим отсечения объекта по пирамиде видимости камеры.
|
||||
* 0 - весь объект
|
||||
* 2 - клиппинг граней по пирамиде видимости камеры
|
||||
*/
|
||||
public var clipping:int = 0;
|
||||
/**
|
||||
* Угол поворота в радианах в плоскости экрана
|
||||
*/
|
||||
public var rotation:Number = 0;
|
||||
/**
|
||||
* Зависимость размера на экране от удалённости от камеры
|
||||
*/
|
||||
public var perspectiveScale:Boolean = true;
|
||||
|
||||
// Вспомогательные
|
||||
static private const vertices:Vector.<Number> = new Vector.<Number>();
|
||||
static private const axes:Vector.<Number> = Vector.<Number>([0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1]);
|
||||
static private const cameraAxes:Vector.<Number> = new Vector.<Number>(12, true);
|
||||
static private const textureMatrix:Matrix = new Matrix();
|
||||
static private var drawTexture:BitmapData;
|
||||
private var projectionX:Number;
|
||||
private var projectionY:Number;
|
||||
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
if (texture == null && mipMap == null) return;
|
||||
var verticesLength:int = calculateVertices(object, camera);
|
||||
if (verticesLength > 0) {
|
||||
var canvas:Canvas = parentCanvas.getChildCanvas(true, false, object.alpha, object.blendMode, object.colorTransform, object.filters);
|
||||
canvas.gfx.beginBitmapFill(drawTexture, textureMatrix, false, smooth);
|
||||
var x:Number = vertices[0]*projectionX;
|
||||
var y:Number = vertices[1]*projectionY;
|
||||
if (rotation == 0) {
|
||||
canvas.gfx.drawRect(x, y, vertices[6]*projectionX - x, vertices[7]*projectionY - y);
|
||||
} else {
|
||||
canvas.gfx.moveTo(x, y);
|
||||
for (var i:int = 3; i < verticesLength; i++) {
|
||||
x = vertices[i]*projectionX; i++;
|
||||
y = vertices[i]*projectionY; i++;
|
||||
canvas.gfx.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override alternativa3d function debug(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
var debugResult:int = camera.checkInDebug(this);
|
||||
if (debugResult == 0) return;
|
||||
var canvas:Canvas = parentCanvas.getChildCanvas(true, false);
|
||||
var i:int, length:int, x:Number, y:Number, t:Number = 0.1;
|
||||
// Рёбра
|
||||
if (debugResult & Debug.EDGES) {
|
||||
if ((length = calculateVertices(object, camera)) > 0) {
|
||||
if (canvas == null) canvas = parentCanvas.getChildCanvas(true, false);
|
||||
canvas.gfx.lineStyle(0, 0xFFFFFF);
|
||||
if (rotation == 0) {
|
||||
x = vertices[0]*projectionX, y = vertices[1]*projectionY;
|
||||
canvas.gfx.drawRect(x, y, vertices[6]*projectionX - x, vertices[7]*projectionY - y);
|
||||
} else {
|
||||
// Отрисовка
|
||||
canvas.gfx.moveTo(vertices[length - 3]*projectionX, vertices[length - 2]*projectionY);
|
||||
for (i = 0; i < length; i++) canvas.gfx.lineTo(vertices[i++]*projectionX, vertices[i++]*projectionY);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Вершины
|
||||
if (debugResult & Debug.VERTICES) {
|
||||
if ((length = calculateVertices(object, camera)) > 0) {
|
||||
if (canvas == null) canvas = parentCanvas.getChildCanvas(true, false);
|
||||
canvas.gfx.lineStyle();
|
||||
if (rotation == 0) {
|
||||
var x1:Number = vertices[0]*projectionX, y1:Number = vertices[1]*projectionY;
|
||||
var x2:Number = vertices[6]*projectionX, y2:Number = vertices[7]*projectionY;
|
||||
if (x1 > -camera.viewSizeX + t && x1 < camera.viewSizeX - t && y1 > -camera.viewSizeY + t && y1 < camera.viewSizeY - t) {
|
||||
canvas.gfx.beginFill(0xFFFF00);
|
||||
canvas.gfx.drawCircle(x1, y1, 2);
|
||||
}
|
||||
if (x1 > -camera.viewSizeX + t && x1 < camera.viewSizeX - t && y2 > -camera.viewSizeY + t && y2 < camera.viewSizeY - t) {
|
||||
canvas.gfx.beginFill(0xFFFF00);
|
||||
canvas.gfx.drawCircle(x1, y2, 2);
|
||||
}
|
||||
if (x2 > -camera.viewSizeX + t && x2 < camera.viewSizeX - t && y2 > -camera.viewSizeY + t && y2 < camera.viewSizeY - t) {
|
||||
canvas.gfx.beginFill(0xFFFF00);
|
||||
canvas.gfx.drawCircle(x2, y2, 2);
|
||||
}
|
||||
if (x2 > -camera.viewSizeX + t && x2 < camera.viewSizeX - t && y1 > -camera.viewSizeY + t && y1 < camera.viewSizeY - t) {
|
||||
canvas.gfx.beginFill(0xFFFF00);
|
||||
canvas.gfx.drawCircle(x2, y1, 2);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < length; i++) {
|
||||
x = vertices[i++]*projectionX, y = vertices[i++]*projectionY;
|
||||
if (x > -camera.viewSizeX + t && x < camera.viewSizeX - t && y > -camera.viewSizeY + t && y < camera.viewSizeY - t) {
|
||||
canvas.gfx.beginFill(0xFFFF00);
|
||||
canvas.gfx.drawCircle(x, y, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Оси, центры, имена, баунды
|
||||
if (debugResult & Debug.AXES) object.drawAxes(camera, canvas);
|
||||
if (debugResult & Debug.CENTERS) object.drawCenter(camera, canvas);
|
||||
if (debugResult & Debug.NAMES) object.drawName(camera, canvas);
|
||||
if (debugResult & Debug.BOUNDS) object.drawBoundBox(camera, canvas);
|
||||
}
|
||||
|
||||
override alternativa3d function getGeometry(camera:Camera3D, object:Object3D):Geometry {
|
||||
if (texture == null && mipMap == null) return null;
|
||||
var verticesLength:int = calculateVertices(object, camera);
|
||||
if (verticesLength > 0) {
|
||||
var geometry:Geometry = Geometry.create();
|
||||
geometry.fragment = Fragment.create();
|
||||
geometry.numVertices = verticesLength/3;
|
||||
geometry.fragment.num = geometry.numVertices;
|
||||
geometry.verticesLength = verticesLength;
|
||||
if (geometry.uvts.length < verticesLength) {
|
||||
geometry.uvts.length = verticesLength;
|
||||
}
|
||||
for (var i:int = 0, j:int = 0; i < geometry.numVertices; i++) {
|
||||
geometry.vertices[j] = vertices[j]; j++;
|
||||
geometry.vertices[j] = vertices[j]; j++;
|
||||
geometry.vertices[j] = vertices[j]; j++;
|
||||
geometry.fragment.indices[i] = i;
|
||||
}
|
||||
geometry.cameraMatrix.identity();
|
||||
geometry.cameraMatrix.prepend(object.cameraMatrix);
|
||||
geometry.alpha = object.alpha;
|
||||
geometry.blendMode = object.blendMode;
|
||||
geometry.colorTransform = object.colorTransform;
|
||||
geometry.filters = object.filters;
|
||||
geometry.sorting = sorting;
|
||||
geometry.texture = drawTexture;
|
||||
geometry.repeatTexture = false;
|
||||
geometry.smooth = smooth;
|
||||
if (camera.debugMode) {
|
||||
geometry.debugResult = camera.checkInDebug(this);
|
||||
}
|
||||
geometry.viewAligned = true;
|
||||
geometry.textureMatrix.a = textureMatrix.a;
|
||||
geometry.textureMatrix.b = textureMatrix.b;
|
||||
geometry.textureMatrix.c = textureMatrix.c;
|
||||
geometry.textureMatrix.d = textureMatrix.d;
|
||||
geometry.textureMatrix.tx = textureMatrix.tx;
|
||||
geometry.textureMatrix.ty = textureMatrix.ty;
|
||||
geometry.projectionX = projectionX;
|
||||
geometry.projectionY = projectionY;
|
||||
return geometry;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function calculateVertices(object:Object3D, camera:Camera3D):int {
|
||||
// Трансформация локальных осей в камеру
|
||||
object.cameraMatrix.transformVectors(axes, cameraAxes);
|
||||
var x:Number = cameraAxes[0];
|
||||
var y:Number = cameraAxes[1];
|
||||
var z:Number = cameraAxes[2];
|
||||
if (z <= camera.nearClipping || z >= camera.farClipping) return 0;
|
||||
// Проекция
|
||||
projectionX = camera.viewSizeX/z;
|
||||
projectionY = camera.viewSizeY/z;
|
||||
var projectionZ:Number = camera.focalLength/z;
|
||||
// Нахождение среднего размера спрайта
|
||||
var ax:Number = (cameraAxes[3] - x)/camera.perspectiveScaleX;
|
||||
var ay:Number = (cameraAxes[4] - y)/camera.perspectiveScaleY;
|
||||
var az:Number = cameraAxes[5] - z;
|
||||
var size:Number = Math.sqrt(ax*ax + ay*ay + az*az);
|
||||
ax = (cameraAxes[6] - x)/camera.perspectiveScaleX;
|
||||
ay = (cameraAxes[7] - y)/camera.perspectiveScaleY;
|
||||
az = cameraAxes[8] - z;
|
||||
size += Math.sqrt(ax*ax + ay*ay + az*az);
|
||||
ax = (cameraAxes[9] - x)/camera.perspectiveScaleX;
|
||||
ay = (cameraAxes[10] - y)/camera.perspectiveScaleY;
|
||||
az = cameraAxes[11] - z;
|
||||
size += Math.sqrt(ax*ax + ay*ay + az*az);
|
||||
size /= 3;
|
||||
// Определение текстуры и коррекция размера
|
||||
if (mipMapping == 0) {
|
||||
drawTexture = texture;
|
||||
} else {
|
||||
var level:int = mipMap.getLevel(z/size, camera);
|
||||
size *= Math.pow(2, level);
|
||||
drawTexture = mipMap.textures[level];
|
||||
}
|
||||
// Учёт флага масштабирования
|
||||
if (!perspectiveScale) {
|
||||
size /= projectionZ;
|
||||
}
|
||||
var x1:Number;
|
||||
var y1:Number;
|
||||
var x2:Number;
|
||||
var y2:Number;
|
||||
// Если не задано вращение
|
||||
if (rotation == 0) {
|
||||
// Размеры спрайта в матрице камеры
|
||||
var cameraWidth:Number = size*drawTexture.width*camera.perspectiveScaleX;
|
||||
var cameraHeight:Number = size*drawTexture.height*camera.perspectiveScaleY;
|
||||
|
||||
// Расчёт вершин в матрице камеры
|
||||
x1 = x - originX*cameraWidth;
|
||||
y1 = y - originY*cameraHeight;
|
||||
x2 = x1 + cameraWidth;
|
||||
y2 = y1 + cameraHeight;
|
||||
|
||||
// Отсечение по вьюпорту
|
||||
if (object.culling > 0 && (x1 > z || y1 > z || x2 < -z || y2 < -z)) return 0;
|
||||
|
||||
// Подготовка матрицы отрисовки
|
||||
textureMatrix.a = textureMatrix.d = size*projectionZ;
|
||||
textureMatrix.b = textureMatrix.c = 0;
|
||||
textureMatrix.tx = x1*projectionX;
|
||||
textureMatrix.ty = y1*projectionY;
|
||||
|
||||
// Подрезка
|
||||
if (clipping == 2) {
|
||||
if (x1 < -z) x1 = -z;
|
||||
if (y1 < -z) y1 = -z;
|
||||
if (x2 > z) x2 = z;
|
||||
if (y2 > z) y2 = z;
|
||||
}
|
||||
|
||||
// Заполняем вершины
|
||||
vertices[0] = x1;
|
||||
vertices[1] = y1;
|
||||
vertices[2] = z;
|
||||
vertices[3] = x1;
|
||||
vertices[4] = y2;
|
||||
vertices[5] = z;
|
||||
vertices[6] = x2;
|
||||
vertices[7] = y2;
|
||||
vertices[8] = z;
|
||||
vertices[9] = x2;
|
||||
vertices[10] = y1;
|
||||
vertices[11] = z;
|
||||
|
||||
return 12;
|
||||
|
||||
} else {
|
||||
|
||||
// Размер спрайта в камере без коррекции под FOV90
|
||||
var textureWidth:Number = drawTexture.width;
|
||||
var textureHeight:Number = drawTexture.height;
|
||||
|
||||
// Расчёт векторов ширины и высоты
|
||||
var sin:Number = Math.sin(rotation)*size;
|
||||
var cos:Number = Math.cos(rotation)*size;
|
||||
var cameraWidthX:Number = cos*textureWidth*camera.perspectiveScaleX;
|
||||
var cameraWidthY:Number = -sin*textureWidth*camera.perspectiveScaleY;
|
||||
var cameraHeightX:Number = sin*textureHeight*camera.perspectiveScaleX;
|
||||
var cameraHeightY:Number = cos*textureHeight*camera.perspectiveScaleY;
|
||||
|
||||
// Заполняем вершины
|
||||
var length:int = 12;
|
||||
vertices[0] = x1 = x - originX*cameraWidthX - originY*cameraHeightX;
|
||||
vertices[1] = y1 = y - originX*cameraWidthY - originY*cameraHeightY;
|
||||
vertices[2] = z;
|
||||
vertices[3] = x1 + cameraHeightX;
|
||||
vertices[4] = y1 + cameraHeightY;
|
||||
vertices[5] = z;
|
||||
vertices[6] = x1 + cameraWidthX + cameraHeightX;
|
||||
vertices[7] = y1 + cameraWidthY + cameraHeightY;
|
||||
vertices[8] = z;
|
||||
vertices[9] = x1 + cameraWidthX;
|
||||
vertices[10] = y1 + cameraWidthY;
|
||||
vertices[11] = z;
|
||||
|
||||
if (object.culling > 0) {
|
||||
// Отсечение по вьюпорту
|
||||
var i:int, infront:Boolean, behind:Boolean, inside:Boolean;
|
||||
var clipLeft:Boolean = false;
|
||||
if (object.culling & 4) {
|
||||
for (i = 0; i < length; i += 3) if ((inside = -vertices[i] < z) && (infront = true) && behind || !inside && (behind = true) && infront) break;
|
||||
if (behind) if (!infront) return 0; else clipLeft = true;
|
||||
infront = false; behind = false;
|
||||
}
|
||||
var clipRight:Boolean = false;
|
||||
if (object.culling & 8) {
|
||||
for (i = 0; i < length; i += 3) if ((inside = vertices[i] < z) && (infront = true) && behind || !inside && (behind = true) && infront) break;
|
||||
if (behind) if (!infront) return 0; else clipRight = true;
|
||||
infront = false; behind = false;
|
||||
}
|
||||
var clipTop:Boolean = false;
|
||||
if (object.culling & 16) {
|
||||
for (i = 1; i < length; i += 3) if ((inside = -vertices[i] < z) && (infront = true) && behind || !inside && (behind = true) && infront) break;
|
||||
if (behind) if (!infront) return 0; else clipTop = true;
|
||||
infront = false; behind = false;
|
||||
}
|
||||
var clipBottom:Boolean = false;
|
||||
if (object.culling & 32) {
|
||||
for (i = 1; i < length; i += 3) if ((inside = vertices[i] < z) && (infront = true) && behind || !inside && (behind = true) && infront) break;
|
||||
if (behind) if (!infront) return 0; else clipBottom = true;
|
||||
}
|
||||
// Подрезка
|
||||
if (clipping == 2) {
|
||||
var n:int = 0, t:Number, bx:Number, by:Number;
|
||||
if (clipLeft) {
|
||||
ax = x = vertices[0]; ay = y = vertices[1];
|
||||
for (i = 3; i <= length; i++) {
|
||||
if (i < length) {
|
||||
bx = vertices[i++]; by = vertices[i++];
|
||||
} else {
|
||||
bx = x; by = y;
|
||||
}
|
||||
if (-bx < z && -ax >= z || -bx >= z && -ax < z) {
|
||||
t = (ax + z)/(ax - bx);
|
||||
vertices[n++] = ax + (bx - ax)*t;
|
||||
vertices[n++] = ay + (by - ay)*t;
|
||||
vertices[n++] = z;
|
||||
}
|
||||
if (-bx < z) {
|
||||
vertices[n++] = bx; vertices[n++] = by; vertices[n++] = z;
|
||||
}
|
||||
ax = bx; ay = by;
|
||||
}
|
||||
if (n == 0) return 0;
|
||||
length = n; n = 0;
|
||||
}
|
||||
if (clipRight) {
|
||||
ax = x = vertices[0]; ay = y = vertices[1];
|
||||
for (i = 3; i <= length; i++) {
|
||||
if (i < length) {
|
||||
bx = vertices[i++]; by = vertices[i++];
|
||||
} else {
|
||||
bx = x; by = y;
|
||||
}
|
||||
if (bx < z && ax >= z || bx >= z && ax < z) {
|
||||
t = (z - ax)/(bx - ax);
|
||||
vertices[n++] = ax + (bx - ax)*t;
|
||||
vertices[n++] = ay + (by - ay)*t;
|
||||
vertices[n++] = z;
|
||||
}
|
||||
if (bx < z) {
|
||||
vertices[n++] = bx; vertices[n++] = by; vertices[n++] = z;
|
||||
}
|
||||
ax = bx; ay = by;
|
||||
}
|
||||
if (n == 0) return 0;
|
||||
length = n; n = 0;
|
||||
}
|
||||
if (clipTop) {
|
||||
ax = x = vertices[0]; ay = y = vertices[1];
|
||||
for (i = 3; i <= length; i++) {
|
||||
if (i < length) {
|
||||
bx = vertices[i++]; by = vertices[i++];
|
||||
} else {
|
||||
bx = x; by = y;
|
||||
}
|
||||
if (-by < z && -ay >= z || -by >= z && -ay < z) {
|
||||
t = (ay + z)/(ay - by);
|
||||
vertices[n++] = ax + (bx - ax)*t;
|
||||
vertices[n++] = ay + (by - ay)*t;
|
||||
vertices[n++] = z;
|
||||
}
|
||||
if (-by < z) {
|
||||
vertices[n++] = bx; vertices[n++] = by; vertices[n++] = z;
|
||||
}
|
||||
ax = bx; ay = by;
|
||||
}
|
||||
if (n == 0) return 0;
|
||||
length = n; n = 0;
|
||||
}
|
||||
if (clipBottom) {
|
||||
ax = x = vertices[0]; ay = y = vertices[1];
|
||||
for (i = 3; i <= length; i++) {
|
||||
if (i < length) {
|
||||
bx = vertices[i++]; by = vertices[i++];
|
||||
} else {
|
||||
bx = x; by = y;
|
||||
}
|
||||
if (by < z && ay >= z || by >= z && ay < z) {
|
||||
t = (z - ay)/(by - ay);
|
||||
vertices[n++] = ax + (bx - ax)*t;
|
||||
vertices[n++] = ay + (by - ay)*t;
|
||||
vertices[n++] = z;
|
||||
}
|
||||
if (by < z) {
|
||||
vertices[n++] = bx; vertices[n++] = by; vertices[n++] = z;
|
||||
}
|
||||
ax = bx; ay = by;
|
||||
}
|
||||
if (n == 0) return 0;
|
||||
length = n; n = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Подготовка матрицы отрисовки
|
||||
textureMatrix.a = textureMatrix.d = cos*projectionZ;
|
||||
textureMatrix.b = -sin*projectionZ;
|
||||
textureMatrix.c = sin*projectionZ;
|
||||
textureMatrix.tx = x1*projectionX;
|
||||
textureMatrix.ty = y1*projectionY;
|
||||
|
||||
return length;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override public function calculateBoundBox(matrix:Matrix3D = null, boundBox:BoundBox = null):BoundBox {
|
||||
// Если указан баунд-бокс
|
||||
if (boundBox != null) {
|
||||
boundBox.infinity();
|
||||
} else {
|
||||
boundBox = new BoundBox();
|
||||
}
|
||||
// Расчёт локального радиуса
|
||||
var t:BitmapData = (mipMapping == 0) ? texture : mipMap.textures[0];
|
||||
var w:Number = ((originX >= 0.5) ? originX : (1 - originX))*t.width;
|
||||
var h:Number = ((originY >= 0.5) ? originY : (1 - originY))*t.height;
|
||||
var radius:Number = Math.sqrt(w*w + h*h);
|
||||
// Если указана матрица трансформации, переводим
|
||||
if (matrix != null) {
|
||||
matrix.transformVectors(axes, cameraAxes);
|
||||
var cz:Number = cameraAxes[2];
|
||||
var cx:Number = cameraAxes[0];
|
||||
var cy:Number = cameraAxes[1];
|
||||
// Нахождение среднего размера спрайта
|
||||
var ax:Number = cameraAxes[3] - cx;
|
||||
var ay:Number = cameraAxes[4] - cy;
|
||||
var az:Number = cameraAxes[5] - cz;
|
||||
var size:Number = Math.sqrt(ax*ax + ay*ay + az*az);
|
||||
ax = cameraAxes[6] - cx;
|
||||
ay = cameraAxes[7] - cy;
|
||||
az = cameraAxes[8] - cz;
|
||||
size += Math.sqrt(ax*ax + ay*ay + az*az);
|
||||
ax = cameraAxes[9] - cx;
|
||||
ay = cameraAxes[10] - cy;
|
||||
az = cameraAxes[11] - cz;
|
||||
size = radius*(size + Math.sqrt(ax*ax + ay*ay + az*az))/3;
|
||||
boundBox.setSize(cx - size, cy - size, cz - size, cx + size, cy + size, cz + size);
|
||||
} else {
|
||||
boundBox.setSize(-radius, -radius, -radius, radius, radius, radius);
|
||||
}
|
||||
return boundBox;
|
||||
}
|
||||
|
||||
public function copyFrom(source:Sprite3D):void {
|
||||
visible = source.visible;
|
||||
alpha = source.alpha;
|
||||
blendMode = source.blendMode;
|
||||
originX = source.originX;
|
||||
originY = source.originY;
|
||||
smooth = source.smooth;
|
||||
clipping = source.clipping;
|
||||
rotation = source.rotation;
|
||||
perspectiveScale = source.perspectiveScale;
|
||||
texture = source.texture;
|
||||
mipMapping = source.mipMapping;
|
||||
mipMap = source.mipMap;
|
||||
matrix.identity();
|
||||
matrix.append(source.matrix);
|
||||
if (source.boundBox != null) {
|
||||
if (boundBox == null) boundBox = new BoundBox();
|
||||
boundBox.copyFrom(source.boundBox);
|
||||
} else boundBox = null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package alternativa.engine3d.objects {
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.geom.Utils3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class WireBoundBox extends Object3D {
|
||||
|
||||
static private const cameraVertices:Vector.<Number> = new Vector.<Number>(24, true);
|
||||
static private const projectedVertices:Vector.<Number> = new Vector.<Number>(16, true);
|
||||
static private const uvts:Vector.<Number> = new Vector.<Number>(24, true);
|
||||
public var thickness:Number = 0;
|
||||
public var color:uint = 0xFFFFFF;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
|
||||
cameraVertices[0] = _boundBox.minX;
|
||||
cameraVertices[1] = _boundBox.minY;
|
||||
cameraVertices[2] = _boundBox.minZ;
|
||||
|
||||
cameraVertices[3] = _boundBox.minX;
|
||||
cameraVertices[4] = _boundBox.minY;
|
||||
cameraVertices[5] = _boundBox.maxZ;
|
||||
|
||||
cameraVertices[6] = _boundBox.minX;
|
||||
cameraVertices[7] = _boundBox.maxY;
|
||||
cameraVertices[8] = _boundBox.minZ;
|
||||
|
||||
cameraVertices[9] = _boundBox.minX;
|
||||
cameraVertices[10] = _boundBox.maxY;
|
||||
cameraVertices[11] = _boundBox.maxZ;
|
||||
|
||||
cameraVertices[12] = _boundBox.maxX;
|
||||
cameraVertices[13] = _boundBox.minY;
|
||||
cameraVertices[14] = _boundBox.minZ;
|
||||
|
||||
cameraVertices[15] = _boundBox.maxX;
|
||||
cameraVertices[16] = _boundBox.minY;
|
||||
cameraVertices[17] = _boundBox.maxZ;
|
||||
|
||||
cameraVertices[18] = _boundBox.maxX;
|
||||
cameraVertices[19] = _boundBox.maxY;
|
||||
cameraVertices[20] = _boundBox.minZ;
|
||||
|
||||
cameraVertices[21] = _boundBox.maxX;
|
||||
cameraVertices[22] = _boundBox.maxY;
|
||||
cameraVertices[23] = _boundBox.maxZ;
|
||||
|
||||
object.cameraMatrix.transformVectors(cameraVertices, cameraVertices);
|
||||
for (var i:int = 0; i < 8; i++) {
|
||||
if (cameraVertices[int(i*3 + 2)] <= 0) return;
|
||||
}
|
||||
|
||||
// Подготовка канваса
|
||||
var canvas:Canvas = parentCanvas.getChildCanvas(true, false, object.alpha, object.blendMode, object.colorTransform, object.filters);
|
||||
|
||||
// Проецируем точки
|
||||
Utils3D.projectVectors(camera.projectionMatrix, cameraVertices, projectedVertices, uvts);
|
||||
|
||||
// Отрисовка
|
||||
canvas.gfx.lineStyle(thickness, color);
|
||||
canvas.gfx.moveTo(projectedVertices[0], projectedVertices[1]);
|
||||
canvas.gfx.lineTo(projectedVertices[2], projectedVertices[3]);
|
||||
canvas.gfx.lineTo(projectedVertices[6], projectedVertices[7]);
|
||||
canvas.gfx.lineTo(projectedVertices[4], projectedVertices[5]);
|
||||
canvas.gfx.lineTo(projectedVertices[0], projectedVertices[1]);
|
||||
canvas.gfx.moveTo(projectedVertices[8], projectedVertices[9]);
|
||||
canvas.gfx.lineTo(projectedVertices[10], projectedVertices[11]);
|
||||
canvas.gfx.lineTo(projectedVertices[14], projectedVertices[15]);
|
||||
canvas.gfx.lineTo(projectedVertices[12], projectedVertices[13]);
|
||||
canvas.gfx.lineTo(projectedVertices[8], projectedVertices[9]);
|
||||
canvas.gfx.moveTo(projectedVertices[0], projectedVertices[1]);
|
||||
canvas.gfx.lineTo(projectedVertices[8], projectedVertices[9]);
|
||||
canvas.gfx.moveTo(projectedVertices[2], projectedVertices[3]);
|
||||
canvas.gfx.lineTo(projectedVertices[10], projectedVertices[11]);
|
||||
canvas.gfx.moveTo(projectedVertices[4], projectedVertices[5]);
|
||||
canvas.gfx.lineTo(projectedVertices[12], projectedVertices[13]);
|
||||
canvas.gfx.moveTo(projectedVertices[6], projectedVertices[7]);
|
||||
canvas.gfx.lineTo(projectedVertices[14], projectedVertices[15]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
45
Alternativa3D7/7.0/alternativa/engine3d/objects/WireQuad.as
Normal file
45
Alternativa3D7/7.0/alternativa/engine3d/objects/WireQuad.as
Normal file
@@ -0,0 +1,45 @@
|
||||
package alternativa.engine3d.objects {
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.geom.Utils3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class WireQuad extends Object3D {
|
||||
|
||||
public var vertices:Vector.<Number>;
|
||||
static private const cameraVertices:Vector.<Number> = new Vector.<Number>(12, true);
|
||||
static private const projectedVertices:Vector.<Number> = new Vector.<Number>(8, true);
|
||||
static private const uvts:Vector.<Number> = new Vector.<Number>(12, true);
|
||||
public var thickness:Number = 0;
|
||||
public var color:uint = 0xFFFFFF;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
object.cameraMatrix.transformVectors(vertices, cameraVertices);
|
||||
for (var i:int = 0; i < 4; i++) {
|
||||
if (cameraVertices[(i*3 + 2)] <= 0) return;
|
||||
}
|
||||
|
||||
// Подготовка канваса
|
||||
var canvas:Canvas = parentCanvas.getChildCanvas(true, false, object.alpha, object.blendMode, object.colorTransform, object.filters);
|
||||
|
||||
// Проецируем точки
|
||||
Utils3D.projectVectors(camera.projectionMatrix, cameraVertices, projectedVertices, uvts);
|
||||
|
||||
// Отрисовка
|
||||
canvas.gfx.lineStyle(thickness, color);
|
||||
canvas.gfx.moveTo(projectedVertices[0], projectedVertices[1]);
|
||||
canvas.gfx.lineTo(projectedVertices[2], projectedVertices[3]);
|
||||
canvas.gfx.lineTo(projectedVertices[4], projectedVertices[5]);
|
||||
canvas.gfx.lineTo(projectedVertices[6], projectedVertices[7]);
|
||||
canvas.gfx.lineTo(projectedVertices[0], projectedVertices[1]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
233
Alternativa3D7/7.0/alternativa/engine3d/primitives/Box.as
Normal file
233
Alternativa3D7/7.0/alternativa/engine3d/primitives/Box.as
Normal file
@@ -0,0 +1,233 @@
|
||||
package alternativa.engine3d.primitives {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class Box extends Mesh {
|
||||
|
||||
public function Box(width:Number = 100, length:Number = 100, height:Number = 100, widthSegments:uint = 1, lengthSegments:uint = 1, heightSegments:uint = 1, reverse:Boolean = false) {
|
||||
|
||||
var wp:uint = widthSegments + 1;
|
||||
var lp:uint = lengthSegments + 1;
|
||||
var hp:uint = heightSegments + 1;
|
||||
|
||||
createEmptyGeometry((wp*(lp + hp) + lp*hp) << 1, (widthSegments*(lengthSegments + heightSegments) + lengthSegments*heightSegments) << 2);
|
||||
|
||||
var wh:Number = width*0.5;
|
||||
var lh:Number = length*0.5;
|
||||
var hh:Number = height*0.5;
|
||||
var wd:Number = 1/widthSegments;
|
||||
var ld:Number = 1/lengthSegments;
|
||||
var hd:Number = 1/heightSegments;
|
||||
var ws:Number = width/widthSegments;
|
||||
var ls:Number = length/lengthSegments;
|
||||
var hs:Number = height/heightSegments;
|
||||
var x:uint;
|
||||
var y:uint;
|
||||
var z:uint;
|
||||
|
||||
var v:uint = 0;
|
||||
var f:uint = 0;
|
||||
|
||||
// Нижняя грань
|
||||
for (x = 0; x < wp; x++) {
|
||||
for (y = 0; y < lp; y++) {
|
||||
vertices[v] = x*ws - wh;
|
||||
uvts[v++] = (widthSegments - x)*wd;
|
||||
vertices[v] = y*ls - lh;
|
||||
uvts[v++] = (lengthSegments - y)*ld;
|
||||
vertices[v++] = -hh;
|
||||
|
||||
if (x < widthSegments && y < lengthSegments) {
|
||||
if (reverse) {
|
||||
indices[f++] = (x + 1)*lp + y + 1;
|
||||
indices[f++] = x*lp + y + 1;
|
||||
indices[f++] = x*lp + y;
|
||||
|
||||
indices[f++] = (x + 1)*lp + y;
|
||||
indices[f++] = (x + 1)*lp + y + 1;
|
||||
indices[f++] = x*lp + y;
|
||||
} else {
|
||||
indices[f++] = x*lp + y;
|
||||
indices[f++] = (x + 1)*lp + y + 1;
|
||||
indices[f++] = (x + 1)*lp + y;
|
||||
|
||||
indices[f++] = x*lp + y;
|
||||
indices[f++] = x*lp + y + 1;
|
||||
indices[f++] = (x + 1)*lp + y + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var o:uint = wp*lp;
|
||||
|
||||
// Верхняя грань
|
||||
for (x = 0; x < wp; x++) {
|
||||
for (y = 0; y < lp; y++) {
|
||||
vertices[v] = x*ws - wh;
|
||||
uvts[v++] = x*wd;
|
||||
vertices[v] = y*ls - lh;
|
||||
uvts[v++] = (lengthSegments - y)*ld;
|
||||
vertices[v++] = hh;
|
||||
|
||||
if (x < widthSegments && y < lengthSegments) {
|
||||
if (reverse) {
|
||||
indices[f++] = o + x*lp + y + 1;
|
||||
indices[f++] = o + (x + 1)*lp + y + 1;
|
||||
indices[f++] = o + x*lp + y;
|
||||
|
||||
indices[f++] = o + (x + 1)*lp + y + 1;
|
||||
indices[f++] = o + (x + 1)*lp + y;
|
||||
indices[f++] = o + x*lp + y;
|
||||
} else {
|
||||
indices[f++] = o + x*lp + y;
|
||||
indices[f++] = o + (x + 1)*lp + y;
|
||||
indices[f++] = o + (x + 1)*lp + y + 1;
|
||||
|
||||
indices[f++] = o + x*lp + y;
|
||||
indices[f++] = o + (x + 1)*lp + y + 1;
|
||||
indices[f++] = o + x*lp + y + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
o += wp*lp;
|
||||
|
||||
// Передняя грань
|
||||
for (x = 0; x < wp; x++) {
|
||||
for (z = 0; z < hp; z++) {
|
||||
vertices[v] = x*ws - wh;
|
||||
uvts[v++] = x*wd;
|
||||
vertices[v] = -lh;
|
||||
uvts[v++] = (heightSegments - z)*hd;
|
||||
vertices[v++] = z*hs - hh;
|
||||
|
||||
if (x < widthSegments && z < heightSegments) {
|
||||
if (reverse) {
|
||||
indices[f++] = o + x*hp + z + 1;
|
||||
indices[f++] = o + (x + 1)*hp + z + 1;
|
||||
indices[f++] = o + x*hp + z;
|
||||
|
||||
indices[f++] = o + (x + 1)*hp + z + 1;
|
||||
indices[f++] = o + (x + 1)*hp + z;
|
||||
indices[f++] = o + x*hp + z;
|
||||
} else {
|
||||
indices[f++] = o + x*hp + z;
|
||||
indices[f++] = o + (x + 1)*hp + z;
|
||||
indices[f++] = o + (x + 1)*hp + z + 1;
|
||||
|
||||
indices[f++] = o + x*hp + z;
|
||||
indices[f++] = o + (x + 1)*hp + z + 1;
|
||||
indices[f++] = o + x*hp + z + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
o += wp*hp;
|
||||
|
||||
// Задняя грань
|
||||
for (x = 0; x < wp; x++) {
|
||||
for (z = 0; z < hp; z++) {
|
||||
vertices[v] = x*ws - wh;
|
||||
uvts[v++] = (widthSegments - x)*wd;
|
||||
vertices[v] = lh;
|
||||
uvts[v++] = (heightSegments - z)*hd;
|
||||
vertices[v++] = z*hs - hh;
|
||||
|
||||
if (x < widthSegments && z < heightSegments) {
|
||||
if (reverse) {
|
||||
indices[f++] = o + (x + 1)*hp + z;
|
||||
indices[f++] = o + (x + 1)*hp + z + 1;
|
||||
indices[f++] = o + x*hp + z + 1;
|
||||
|
||||
indices[f++] = o + (x + 1)*hp + z;
|
||||
indices[f++] = o + x*hp + z + 1;
|
||||
indices[f++] = o + x*hp + z;
|
||||
} else {
|
||||
indices[f++] = o + x*hp + z;
|
||||
indices[f++] = o + x*hp + z + 1;
|
||||
indices[f++] = o + (x + 1)*hp + z;
|
||||
|
||||
indices[f++] = o + x*hp + z + 1;
|
||||
indices[f++] = o + (x + 1)*hp + z + 1;
|
||||
indices[f++] = o + (x + 1)*hp + z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
o += wp*hp;
|
||||
|
||||
// Левая грань
|
||||
for (y = 0; y < lp; y++) {
|
||||
for (z = 0; z < hp; z++) {
|
||||
vertices[v] = -wh;
|
||||
uvts[v++] = (lengthSegments - y)*ld;
|
||||
vertices[v] = y*ls - lh;
|
||||
uvts[v++] = (heightSegments - z)*hd;
|
||||
vertices[v++] = z*hs - hh;
|
||||
|
||||
if (y < lengthSegments && z < heightSegments) {
|
||||
if (reverse) {
|
||||
indices[f++] = o + (y + 1)*hp + z;
|
||||
indices[f++] = o + (y + 1)*hp + z + 1;
|
||||
indices[f++] = o + y*hp + z + 1;
|
||||
|
||||
indices[f++] = o + (y + 1)*hp + z;
|
||||
indices[f++] = o + y*hp + z + 1;
|
||||
indices[f++] = o + y*hp + z;
|
||||
} else {
|
||||
indices[f++] = o + y*hp + z;
|
||||
indices[f++] = o + y*hp + z + 1;
|
||||
indices[f++] = o + (y + 1)*hp + z;
|
||||
|
||||
indices[f++] = o + y*hp + z + 1;
|
||||
indices[f++] = o + (y + 1)*hp + z + 1;
|
||||
indices[f++] = o + (y + 1)*hp + z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
o += lp*hp;
|
||||
|
||||
// Правая грань
|
||||
for (y = 0; y < lp; y++) {
|
||||
for (z = 0; z < hp; z++) {
|
||||
vertices[v] = wh;
|
||||
uvts[v++] = y*ld;
|
||||
vertices[v] = y*ls - lh;
|
||||
uvts[v++] = (heightSegments - z)*hd;
|
||||
vertices[v++] = z*hs - hh;
|
||||
|
||||
if (y < lengthSegments && z < heightSegments) {
|
||||
if (reverse) {
|
||||
indices[f++] = o + y*hp + z + 1;
|
||||
indices[f++] = o + (y + 1)*hp + z + 1;
|
||||
indices[f++] = o + y*hp + z;
|
||||
|
||||
indices[f++] = o + (y + 1)*hp + z + 1;
|
||||
indices[f++] = o + (y + 1)*hp + z;
|
||||
indices[f++] = o + y*hp + z;
|
||||
} else {
|
||||
indices[f++] = o + y*hp + z;
|
||||
indices[f++] = o + (y + 1)*hp + z;
|
||||
indices[f++] = o + (y + 1)*hp + z + 1;
|
||||
|
||||
indices[f++] = o + y*hp + z;
|
||||
indices[f++] = o + (y + 1)*hp + z + 1;
|
||||
indices[f++] = o + y*hp + z + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Установка границ
|
||||
_boundBox = new BoundBox();
|
||||
_boundBox.setSize(-wh, -lh, -hh, wh, lh, hh);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
244
Alternativa3D7/7.0/alternativa/engine3d/primitives/GeoSphere.as
Normal file
244
Alternativa3D7/7.0/alternativa/engine3d/primitives/GeoSphere.as
Normal file
@@ -0,0 +1,244 @@
|
||||
package alternativa.engine3d.primitives {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class GeoSphere extends Mesh {
|
||||
|
||||
public function GeoSphere(radius:Number = 100, segments:uint = 2, reverse:Boolean = false) {
|
||||
|
||||
const sections:uint = 20;
|
||||
|
||||
var i:uint;
|
||||
|
||||
var theta:Number;
|
||||
var sin:Number;
|
||||
var cos:Number;
|
||||
// z расстояние до нижней и верхней крышки полюса
|
||||
var subz:Number = 4.472136E-001*radius;
|
||||
// радиус на расстоянии subz
|
||||
var subrad:Number = 2*subz;
|
||||
|
||||
var v:uint = 0;
|
||||
|
||||
var f:uint = sections*segments*segments;
|
||||
createEmptyGeometry(f/2 + 2, f);
|
||||
|
||||
vertices[v++] = 0;
|
||||
vertices[v++] = 0;
|
||||
vertices[v++] = radius;
|
||||
|
||||
// Создание вершин верхней крышки
|
||||
for (i = 0; i < 5; i++) {
|
||||
theta = Math.PI*2*i/5;
|
||||
sin = Math.sin(theta);
|
||||
cos = Math.cos(theta);
|
||||
vertices[v++] = subrad*cos;
|
||||
vertices[v++] = subrad*sin;
|
||||
vertices[v++] = subz;
|
||||
}
|
||||
// Создание вершин нижней крышки
|
||||
for (i = 0; i < 5; i++) {
|
||||
theta = Math.PI*((i << 1) + 1)/5;
|
||||
sin = Math.sin(theta);
|
||||
cos = Math.cos(theta);
|
||||
vertices[v++] = subrad*cos;
|
||||
vertices[v++] = subrad*sin;
|
||||
vertices[v++] = -subz;
|
||||
}
|
||||
|
||||
vertices[v++] = 0;
|
||||
vertices[v++] = 0;
|
||||
vertices[v++] = -radius;
|
||||
|
||||
for (i = 1; i < 6; i++) {
|
||||
v = interpolate(0, i, segments, v);
|
||||
}
|
||||
for (i = 1; i < 6; i++) {
|
||||
v = interpolate(i, i % 5 + 1, segments, v);
|
||||
}
|
||||
for (i = 1; i < 6; i++) {
|
||||
v = interpolate(i, i + 5, segments, v);
|
||||
}
|
||||
for (i = 1; i < 6; i++) {
|
||||
v = interpolate(i, (i + 3) % 5 + 6, segments, v);
|
||||
}
|
||||
for (i = 1; i < 6; i++) {
|
||||
v = interpolate(i + 5, i % 5 + 6, segments, v);
|
||||
}
|
||||
for (i = 6; i < 11; i++) {
|
||||
v = interpolate(11, i, segments, v);
|
||||
}
|
||||
for (f = 0; f < 5; f++) {
|
||||
for (i = 1; i <= segments - 2; i++) {
|
||||
v = interpolate(12 + f*(segments - 1) + i, 12 + (f + 1) % 5*(segments - 1) + i, i + 1, v);
|
||||
}
|
||||
}
|
||||
for (f = 0; f < 5; f++) {
|
||||
for (i = 1; i <= segments - 2; i++) {
|
||||
v = interpolate(12 + (f + 15)*(segments - 1) + i, 12 + (f + 10)*(segments - 1) + i, i + 1, v);
|
||||
}
|
||||
}
|
||||
for (f = 0; f < 5; f++) {
|
||||
for (i = 1; i <= segments - 2; i++) {
|
||||
v = interpolate(12 + ((f + 1) % 5 + 15)*(segments - 1) + segments - 2 - i, 12 + (f + 10)*(segments - 1) + segments - 2 - i, i + 1, v);
|
||||
}
|
||||
}
|
||||
for (f = 0; f < 5; f++) {
|
||||
for (i = 1; i <= segments - 2; i++) {
|
||||
v = interpolate(12 + ((f + 1) % 5 + 25)*(segments - 1) + i, 12 + (f + 25)*(segments - 1) + i, i + 1, v);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < numVertices; i++) {
|
||||
var j:uint = i*3;
|
||||
uvts[j] = Math.atan2(vertices[j + 1], vertices[j])/(Math.PI*2);
|
||||
uvts[j] = 0.5 + (reverse ? -uvts[j] : uvts[j]);
|
||||
uvts[j + 1] = 0.5 + Math.asin(vertices[j + 2]/radius)/Math.PI;
|
||||
}
|
||||
|
||||
var num:uint = 0;
|
||||
for (f = 0; f <= sections - 1; f++) {
|
||||
for (var row:uint = 0; row <= segments - 1; row++) {
|
||||
for (var column:uint = 0; column <= row; column++) {
|
||||
var a:uint = findVertices(segments, f, row, column);
|
||||
var b:uint = findVertices(segments, f, row + 1, column);
|
||||
var c:uint = findVertices(segments, f, row + 1, column + 1);
|
||||
|
||||
if (reverse) {
|
||||
indices[num++] = a;
|
||||
indices[num++] = c;
|
||||
indices[num++] = b;
|
||||
} else {
|
||||
indices[num++] = a;
|
||||
indices[num++] = b;
|
||||
indices[num++] = c;
|
||||
}
|
||||
|
||||
if (column < row) {
|
||||
var d:uint = findVertices(segments, f, row, column + 1);
|
||||
if (reverse) {
|
||||
indices[num++] = a;
|
||||
indices[num++] = d;
|
||||
indices[num++] = c;
|
||||
} else {
|
||||
indices[num++] = a;
|
||||
indices[num++] = c;
|
||||
indices[num++] = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Установка границ
|
||||
_boundBox = new BoundBox();
|
||||
_boundBox.setSize(-radius, -radius, -radius, radius, radius, radius);
|
||||
}
|
||||
|
||||
private function interpolate(a:uint, b:uint, num:uint, v:uint):uint {
|
||||
if (num < 2) {
|
||||
return v;
|
||||
}
|
||||
a *= 3;
|
||||
b *= 3;
|
||||
var ax:Number = vertices[a];
|
||||
var ay:Number = vertices[a + 1];
|
||||
var az:Number = vertices[a + 2];
|
||||
var bx:Number = vertices[b];
|
||||
var by:Number = vertices[b + 1];
|
||||
var bz:Number = vertices[b + 2];
|
||||
var cos:Number = (ax*bx + ay*by + az*bz)/(ax*ax + ay*ay + az*az);
|
||||
cos = (cos < -1) ? -1 : ((cos > 1) ? 1 : cos);
|
||||
var theta:Number = Math.acos(cos);
|
||||
var sin:Number = Math.sin(theta);
|
||||
for (var e:uint = 1; e < num; e++) {
|
||||
var theta1:Number = theta*e/num;
|
||||
var theta2:Number = theta*(num - e)/num;
|
||||
var st1:Number = Math.sin(theta1);
|
||||
var st2:Number = Math.sin(theta2);
|
||||
vertices[v++] = (ax*st2 + bx*st1)/sin;
|
||||
vertices[v++] = (ay*st2 + by*st1)/sin;
|
||||
vertices[v++] = (az*st2 + bz*st1)/sin;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
private function findVertices(segments:uint, section:uint, row:uint, column:uint):uint {
|
||||
if (row == 0) {
|
||||
if (section < 5) {
|
||||
return (0);
|
||||
}
|
||||
if (section > 14) {
|
||||
return (11);
|
||||
}
|
||||
return (section - 4);
|
||||
}
|
||||
if (row == segments && column == 0) {
|
||||
if (section < 5) {
|
||||
return (section + 1);
|
||||
}
|
||||
if (section < 10) {
|
||||
return ((section + 4) % 5 + 6);
|
||||
}
|
||||
if (section < 15) {
|
||||
return ((section + 1) % 5 + 1);
|
||||
}
|
||||
return ((section + 1) % 5 + 6);
|
||||
}
|
||||
if (row == segments && column == segments) {
|
||||
if (section < 5) {
|
||||
return ((section + 1) % 5 + 1);
|
||||
}
|
||||
if (section < 10) {
|
||||
return (section + 1);
|
||||
}
|
||||
if (section < 15) {
|
||||
return (section - 9);
|
||||
}
|
||||
return (section - 9);
|
||||
}
|
||||
if (row == segments) {
|
||||
if (section < 5) {
|
||||
return (12 + (5 + section)*(segments - 1) + column - 1);
|
||||
}
|
||||
if (section < 10) {
|
||||
return (12 + (20 + (section + 4) % 5)*(segments - 1) + column - 1);
|
||||
}
|
||||
if (section < 15) {
|
||||
return (12 + (section - 5)*(segments - 1) + segments - 1 - column);
|
||||
}
|
||||
return (12 + (5 + section)*(segments - 1) + segments - 1 - column);
|
||||
}
|
||||
if (column == 0) {
|
||||
if (section < 5) {
|
||||
return (12 + section*(segments - 1) + row - 1);
|
||||
}
|
||||
if (section < 10) {
|
||||
return (12 + (section % 5 + 15)*(segments - 1) + row - 1);
|
||||
}
|
||||
if (section < 15) {
|
||||
return (12 + ((section + 1) % 5 + 15)*(segments - 1) + segments - 1 - row);
|
||||
}
|
||||
return (12 + ((section + 1) % 5 + 25)*(segments - 1) + row - 1);
|
||||
}
|
||||
if (column == row) {
|
||||
if (section < 5) {
|
||||
return (12 + (section + 1) % 5*(segments - 1) + row - 1);
|
||||
}
|
||||
if (section < 10) {
|
||||
return (12 + (section % 5 + 10)*(segments - 1) + row - 1);
|
||||
}
|
||||
if (section < 15) {
|
||||
return (12 + (section % 5 + 10)*(segments - 1) + segments - row - 1);
|
||||
}
|
||||
return (12 + (section % 5 + 25)*(segments - 1) + row - 1);
|
||||
}
|
||||
return (12 + 30*(segments - 1) + section*(segments - 1)*(segments - 2)/2 + (row - 1)*(row - 2)/2 + column - 1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
67
Alternativa3D7/7.0/alternativa/engine3d/primitives/Plane.as
Normal file
67
Alternativa3D7/7.0/alternativa/engine3d/primitives/Plane.as
Normal file
@@ -0,0 +1,67 @@
|
||||
package alternativa.engine3d.primitives {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Plane extends Mesh {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param width
|
||||
* @param length
|
||||
* @param widthSegments
|
||||
* @param lengthSegments
|
||||
*/
|
||||
public function Plane(width:Number = 100, length:Number = 100, widthSegments:uint = 1, lengthSegments:uint = 1) {
|
||||
|
||||
var wp:uint = widthSegments + 1;
|
||||
var lp:uint = lengthSegments + 1;
|
||||
|
||||
createEmptyGeometry(wp*lp, (widthSegments*lengthSegments) << 2);
|
||||
|
||||
var wh:Number = width*0.5;
|
||||
var lh:Number = length*0.5;
|
||||
var wd:Number = 1/widthSegments;
|
||||
var ld:Number = 1/lengthSegments;
|
||||
var ws:Number = width/widthSegments;
|
||||
var ls:Number = length/lengthSegments;
|
||||
var x:uint;
|
||||
var y:uint;
|
||||
var z:uint;
|
||||
|
||||
var v:uint = 0;
|
||||
var f:uint = 0;
|
||||
|
||||
// Верхняя грань
|
||||
for (x = 0; x < wp; x++) {
|
||||
for (y = 0; y < lp; y++) {
|
||||
vertices[v] = x*ws - wh;
|
||||
uvts[v++] = x*wd;
|
||||
vertices[v] = y*ls - lh;
|
||||
uvts[v++] = (lengthSegments - y)*ld;
|
||||
vertices[v++] = 0;
|
||||
|
||||
if (x < widthSegments && y < lengthSegments) {
|
||||
indices[f++] = x*lp + y;
|
||||
indices[f++] = (x + 1)*lp + y;
|
||||
indices[f++] = (x + 1)*lp + y + 1;
|
||||
|
||||
indices[f++] = x*lp + y;
|
||||
indices[f++] = (x + 1)*lp + y + 1;
|
||||
indices[f++] = x*lp + y + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Установка границ
|
||||
_boundBox = new BoundBox();
|
||||
_boundBox.setSize(-wh, -lh, 0, wh, lh, 0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
14
Alternativa3D7/7.1/alternativa/Alternativa3D.as
Normal file
14
Alternativa3D7/7.1/alternativa/Alternativa3D.as
Normal file
@@ -0,0 +1,14 @@
|
||||
package alternativa {
|
||||
|
||||
/**
|
||||
* Класс содержит информацию о версии библиотеки.
|
||||
* Также используется для интеграции библиотеки в среду разработки Adobe Flash.
|
||||
*/
|
||||
public class Alternativa3D {
|
||||
|
||||
/**
|
||||
* Версия библиотеки в формате: поколение.feature-версия.fix-версия
|
||||
*/
|
||||
public static const version:String = "7.0.0";
|
||||
}
|
||||
}
|
||||
3
Alternativa3D7/7.1/alternativa/engine3d/alternativa3d.as
Normal file
3
Alternativa3D7/7.1/alternativa/engine3d/alternativa3d.as
Normal file
@@ -0,0 +1,3 @@
|
||||
package alternativa.engine3d {
|
||||
public namespace alternativa3d = "http://alternativaplatform.com/en/alternativa3d";
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package alternativa.engine3d.containers {
|
||||
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import flash.geom.Vector3D;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.core.Object3DContainer;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Контейнер, дочерние объекты которого отрисовываются по удалённости от камеры
|
||||
*/
|
||||
public class AverageZContainer extends Object3DContainer {
|
||||
|
||||
static private const averageZ:Vector.<Number> = new Vector.<Number>();
|
||||
static private const center:Vector.<Number> = Vector.<Number>([0, 0, 0]);
|
||||
static private const cameraCenter:Vector.<Number> = new Vector.<Number>(3, true);
|
||||
static private const sortingStack:Vector.<int> = new Vector.<int>();
|
||||
private var sortingStackIndex:int;
|
||||
private var sortingLeft:Number;
|
||||
private var sortingMedian:Number;
|
||||
private var sortingRight:Number;
|
||||
private var sortingChild:Object3D;
|
||||
|
||||
override protected function calculateOrder(camera:Camera3D, object:Object3D):void {
|
||||
|
||||
// Сортировка
|
||||
for (var i:int = 0; i < numVisibleChildren; i++) {
|
||||
(visibleChildren[i] as Object3D).cameraMatrix.transformVectors(center, cameraCenter);
|
||||
averageZ[i] = cameraCenter[0]*cameraCenter[0] + cameraCenter[1]*cameraCenter[1] + cameraCenter[2]*cameraCenter[2];
|
||||
}
|
||||
|
||||
var j:int, l:int = 0, r:int = numVisibleChildren - 1;
|
||||
sortingStack[0] = l;
|
||||
sortingStack[1] = r;
|
||||
sortingStackIndex = 2;
|
||||
while (sortingStackIndex > 0) {
|
||||
j = r = sortingStack[--sortingStackIndex];
|
||||
i = l = sortingStack[--sortingStackIndex];
|
||||
sortingMedian = averageZ[(r + l) >> 1];
|
||||
do {
|
||||
while ((sortingLeft = averageZ[i]) > sortingMedian) i++;
|
||||
while ((sortingRight = averageZ[j]) < sortingMedian) j--;
|
||||
if (i <= j) {
|
||||
sortingChild = visibleChildren[i];
|
||||
visibleChildren[i] = visibleChildren[j];
|
||||
visibleChildren[j] = sortingChild;
|
||||
averageZ[i++] = sortingRight;
|
||||
averageZ[j--] = sortingLeft;
|
||||
}
|
||||
} while (i <= j);
|
||||
if (l < j) {
|
||||
sortingStack[sortingStackIndex++] = l;
|
||||
sortingStack[sortingStackIndex++] = j;
|
||||
}
|
||||
if (i < r) {
|
||||
sortingStack[sortingStackIndex++] = i;
|
||||
sortingStack[sortingStackIndex++] = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package alternativa.engine3d.containers {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.geom.Vector3D;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.core.Object3DContainer;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class DirectionContainer extends Object3DContainer {
|
||||
|
||||
public var direction:Vector3D = new Vector3D(0, 0, -1);
|
||||
|
||||
override protected function calculateOrder(camera:Camera3D, object:Object3D):void {
|
||||
if (Vector3D.Z_AXIS.dotProduct(object.cameraMatrix.deltaTransformVector(direction)) < 0) {
|
||||
var num:uint = numVisibleChildren >> 1;
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
var child:Object3D = visibleChildren[i];
|
||||
visibleChildren[i] = visibleChildren[numVisibleChildren - 1 - i];
|
||||
visibleChildren[numVisibleChildren - 1 - i] = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
1235
Alternativa3D7/7.1/alternativa/engine3d/containers/KDTree.as
Normal file
1235
Alternativa3D7/7.1/alternativa/engine3d/containers/KDTree.as
Normal file
File diff suppressed because it is too large
Load Diff
156
Alternativa3D7/7.1/alternativa/engine3d/containers/SkyBox.as
Normal file
156
Alternativa3D7/7.1/alternativa/engine3d/containers/SkyBox.as
Normal file
@@ -0,0 +1,156 @@
|
||||
package alternativa.engine3d.containers {
|
||||
import flash.display.BitmapData;
|
||||
import __AS3__.vec.Vector;
|
||||
import alternativa.engine3d.core.Object3DContainer;
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
|
||||
public class SkyBox extends Object3DContainer {
|
||||
|
||||
private var backPlane:Mesh;
|
||||
private var frontPlane:Mesh;
|
||||
private var topPlane:Mesh;
|
||||
private var bottomPlane:Mesh;
|
||||
private var leftPlane:Mesh;
|
||||
private var rightPlane:Mesh;
|
||||
|
||||
public function SkyBox():void {
|
||||
backPlane = new Mesh();
|
||||
frontPlane = new Mesh();
|
||||
topPlane = new Mesh();
|
||||
bottomPlane = new Mesh();
|
||||
leftPlane = new Mesh();
|
||||
rightPlane = new Mesh();
|
||||
|
||||
backPlane.clipping = 2;
|
||||
frontPlane.clipping = 2;
|
||||
topPlane.clipping = 2;
|
||||
bottomPlane.clipping = 2;
|
||||
leftPlane.clipping = 2;
|
||||
rightPlane.clipping = 2;
|
||||
|
||||
backPlane.perspectiveCorrection = true;
|
||||
frontPlane.perspectiveCorrection = true;
|
||||
topPlane.perspectiveCorrection = true;
|
||||
bottomPlane.perspectiveCorrection = true;
|
||||
leftPlane.perspectiveCorrection = true;
|
||||
rightPlane.perspectiveCorrection = true;
|
||||
|
||||
const size:Number = 1000;
|
||||
|
||||
backPlane.vertices = Vector.<Number>([
|
||||
-size, -size, -size,
|
||||
-size, -size, size,
|
||||
size, -size, size,
|
||||
size, -size, -size,
|
||||
]);
|
||||
frontPlane.vertices = Vector.<Number>([
|
||||
size, size, -size,
|
||||
size, size, size,
|
||||
-size, size, size,
|
||||
-size, size, -size,
|
||||
]);
|
||||
topPlane.vertices = Vector.<Number>([
|
||||
size, size, size,
|
||||
size, -size, size,
|
||||
-size, -size, size,
|
||||
-size, size, size,
|
||||
]);
|
||||
bottomPlane.vertices = Vector.<Number>([
|
||||
size, -size, -size,
|
||||
size, size, -size,
|
||||
-size, size, -size,
|
||||
-size, -size, -size,
|
||||
]);
|
||||
leftPlane.vertices = Vector.<Number>([
|
||||
-size, size, -size,
|
||||
-size, size, size,
|
||||
-size, -size, size,
|
||||
-size, -size, -size,
|
||||
]);
|
||||
rightPlane.vertices = Vector.<Number>([
|
||||
size, -size, -size,
|
||||
size, -size, size,
|
||||
size, size, size,
|
||||
size, size, -size,
|
||||
]);
|
||||
|
||||
var uvts:Vector.<Number> = Vector.<Number>([1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0]);
|
||||
backPlane.uvts = uvts;
|
||||
frontPlane.uvts = uvts;
|
||||
topPlane.uvts = uvts;
|
||||
bottomPlane.uvts = uvts;
|
||||
leftPlane.uvts = uvts;
|
||||
rightPlane.uvts = uvts;
|
||||
|
||||
var uvs:Vector.<Number> = Vector.<Number>([1, 1, 1, 0, 0, 0, 0, 1]);
|
||||
backPlane.uvs = uvs;
|
||||
frontPlane.uvs = uvs;
|
||||
topPlane.uvs = uvs;
|
||||
bottomPlane.uvs = uvs;
|
||||
leftPlane.uvs = uvs;
|
||||
rightPlane.uvs = uvs;
|
||||
|
||||
var indices:Vector.<int> = Vector.<int>([4, 0, 1, 2, 3]);
|
||||
backPlane.indices = indices;
|
||||
frontPlane.indices = indices;
|
||||
topPlane.indices = indices;
|
||||
bottomPlane.indices = indices;
|
||||
leftPlane.indices = indices;
|
||||
rightPlane.indices = indices;
|
||||
|
||||
backPlane.numVertices = 4;
|
||||
frontPlane.numVertices = 4;
|
||||
topPlane.numVertices = 4;
|
||||
bottomPlane.numVertices = 4;
|
||||
leftPlane.numVertices = 4;
|
||||
rightPlane.numVertices = 4;
|
||||
|
||||
backPlane.numFaces = 1;
|
||||
frontPlane.numFaces = 1;
|
||||
topPlane.numFaces = 1;
|
||||
bottomPlane.numFaces = 1;
|
||||
leftPlane.numFaces = 1;
|
||||
rightPlane.numFaces = 1;
|
||||
|
||||
backPlane.poly = true;
|
||||
frontPlane.poly = true;
|
||||
topPlane.poly = true;
|
||||
bottomPlane.poly = true;
|
||||
leftPlane.poly = true;
|
||||
rightPlane.poly = true;
|
||||
|
||||
addChild(backPlane);
|
||||
addChild(frontPlane);
|
||||
addChild(topPlane);
|
||||
addChild(bottomPlane);
|
||||
addChild(leftPlane);
|
||||
addChild(rightPlane);
|
||||
}
|
||||
|
||||
public function set backTexture(value:BitmapData):void {
|
||||
backPlane.texture = value;
|
||||
}
|
||||
|
||||
public function set frontTexture(value:BitmapData):void {
|
||||
frontPlane.texture = value;
|
||||
}
|
||||
|
||||
public function set topTexture(value:BitmapData):void {
|
||||
topPlane.texture = value;
|
||||
}
|
||||
|
||||
public function set bottomTexture(value:BitmapData):void {
|
||||
bottomPlane.texture = value;
|
||||
}
|
||||
|
||||
public function set leftTexture(value:BitmapData):void {
|
||||
leftPlane.texture = value;
|
||||
}
|
||||
|
||||
public function set rightTexture(value:BitmapData):void {
|
||||
rightPlane.texture = value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package alternativa.engine3d.containers {
|
||||
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.core.Object3DContainer;
|
||||
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class SplitContainer extends Object3DContainer {
|
||||
|
||||
public var splitPlane:Vector3D = new Vector3D(0, 0, 1, 0);
|
||||
static private const invertCameraMatrix:Matrix3D = new Matrix3D();
|
||||
static private const cameraPosition:Vector.<Number> = Vector.<Number>([0, 0, 0]);
|
||||
static private const invertCameraPosition:Vector.<Number> = new Vector.<Number>(3, true);
|
||||
|
||||
override protected function calculateOrder(camera:Camera3D, object:Object3D):void {
|
||||
invertCameraMatrix.identity();
|
||||
invertCameraMatrix.prepend(object.cameraMatrix);
|
||||
invertCameraMatrix.invert();
|
||||
invertCameraMatrix.transformVectors(cameraPosition, invertCameraPosition);
|
||||
if (invertCameraPosition[0]*splitPlane.x + invertCameraPosition[1]*splitPlane.y + invertCameraPosition[2]*splitPlane.z < splitPlane.w) {
|
||||
var num:uint = numVisibleChildren >> 1;
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
var child:Object3D = visibleChildren[i];
|
||||
visibleChildren[i] = visibleChildren[numVisibleChildren - 1 - i];
|
||||
visibleChildren[numVisibleChildren - 1 - i] = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,454 @@
|
||||
package alternativa.engine3d.controllers {
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.events.KeyboardEvent;
|
||||
import flash.events.MouseEvent;
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Vector3D;
|
||||
import flash.ui.Keyboard;
|
||||
import flash.utils.getTimer;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class SimpleObjectController {
|
||||
|
||||
/**
|
||||
* Имя действия для привязки клавиш движения вперёд.
|
||||
*/
|
||||
public static const ACTION_FORWARD:String = "ACTION_FORWARD";
|
||||
/**
|
||||
* Имя действия для привязки клавиш движения назад.
|
||||
*/
|
||||
public static const ACTION_BACK:String = "ACTION_BACK";
|
||||
/**
|
||||
* Имя действия для привязки клавиш движения влево.
|
||||
*/
|
||||
public static const ACTION_LEFT:String = "ACTION_LEFT";
|
||||
/**
|
||||
* Имя действия для привязки клавиш движения вправо.
|
||||
*/
|
||||
public static const ACTION_RIGHT:String = "ACTION_RIGHT";
|
||||
/**
|
||||
* Имя действия для привязки клавиш движения вверх.
|
||||
*/
|
||||
public static const ACTION_UP:String = "ACTION_UP";
|
||||
/**
|
||||
* Имя действия для привязки клавиш движения вниз.
|
||||
*/
|
||||
public static const ACTION_DOWN:String = "ACTION_DOWN";
|
||||
/**
|
||||
* Имя действия для привязки клавиш поворота вверх.
|
||||
*/
|
||||
public static const ACTION_PITCH_UP:String = "ACTION_PITCH_UP";
|
||||
/**
|
||||
* Имя действия для привязки клавиш поворота вниз.
|
||||
*/
|
||||
public static const ACTION_PITCH_DOWN:String = "ACTION_PITCH_DOWN";
|
||||
/**
|
||||
* Имя действия для привязки клавиш поворота налево.
|
||||
*/
|
||||
public static const ACTION_YAW_LEFT:String = "ACTION_YAW_LEFT";
|
||||
/**
|
||||
* Имя действия для привязки клавиш поворота направо.
|
||||
*/
|
||||
public static const ACTION_YAW_RIGHT:String = "ACTION_YAW_RIGHT";
|
||||
/**
|
||||
* Имя действия для привязки клавиш увеличения скорости.
|
||||
*/
|
||||
public static const ACTION_ACCELERATE:String = "ACTION_ACCELERATE";
|
||||
/**
|
||||
* Имя действия для привязки клавиш активации обзора мышью.
|
||||
*/
|
||||
public static const ACTION_MOUSE_LOOK:String = "ACTION_MOUSE_LOOK";
|
||||
|
||||
|
||||
public var speed:Number;
|
||||
public var speedMultiplier:Number;
|
||||
public var mouseSensitivity:Number;
|
||||
public var maxPitch:Number = Number.MAX_VALUE;
|
||||
public var minPitch:Number = -Number.MAX_VALUE;
|
||||
|
||||
private var eventSource:InteractiveObject;
|
||||
private var _object:Object3D;
|
||||
|
||||
private var _up:Boolean;
|
||||
private var _down:Boolean;
|
||||
private var _forward:Boolean;
|
||||
private var _back:Boolean;
|
||||
private var _left:Boolean;
|
||||
private var _right:Boolean;
|
||||
private var _accelerate:Boolean;
|
||||
|
||||
private var displacement:Vector3D = new Vector3D();
|
||||
private var mousePoint:Point = new Point();
|
||||
private var mouseLook:Boolean;
|
||||
private var objectTransform:Vector.<Vector3D>;
|
||||
|
||||
private var time:int;
|
||||
|
||||
/**
|
||||
* Ассоциативный массив, связывающий имена команд с реализующими их функциями. Функции должны иметь вид
|
||||
* function(value:Boolean):void. Значение параметра <code>value</code> указывает, нажата или отпущена соответсвующая команде
|
||||
* клавиша.
|
||||
*/
|
||||
private var actionBindings:Object = {};
|
||||
/**
|
||||
* Ассоциативный массив, связывающий коды клавиатурных клавиш с именами команд.
|
||||
*/
|
||||
protected var keyBindings:Object = {};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param eventSource источник событий для контроллера
|
||||
* @param speed скорость поступательного перемещения объекта
|
||||
* @param mouseSensitivity чувствительность мыши - количество градусов поворота на один пиксель перемещения мыши
|
||||
*/
|
||||
public function SimpleObjectController(eventSource:InteractiveObject, object:Object3D, speed:Number, speedMultiplier:Number = 3, mouseSensitivity:Number = 1) {
|
||||
this.eventSource = eventSource;
|
||||
this.object = object;
|
||||
this.speed = speed;
|
||||
this.speedMultiplier = speedMultiplier;
|
||||
this.mouseSensitivity = mouseSensitivity;
|
||||
|
||||
actionBindings[ACTION_FORWARD] = moveForward;
|
||||
actionBindings[ACTION_BACK] = moveBack;
|
||||
actionBindings[ACTION_LEFT] = moveLeft;
|
||||
actionBindings[ACTION_RIGHT] = moveRight;
|
||||
actionBindings[ACTION_UP] = moveUp;
|
||||
actionBindings[ACTION_DOWN] = moveDown;
|
||||
actionBindings[ACTION_ACCELERATE] = accelerate;
|
||||
|
||||
setDefaultBindings();
|
||||
|
||||
enable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Активирует контроллер.
|
||||
*/
|
||||
public function enable():void {
|
||||
eventSource.addEventListener(KeyboardEvent.KEY_DOWN, onKey);
|
||||
eventSource.addEventListener(KeyboardEvent.KEY_UP, onKey);
|
||||
eventSource.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
||||
eventSource.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Дективирует контроллер.
|
||||
*/
|
||||
public function disable():void {
|
||||
eventSource.removeEventListener(KeyboardEvent.KEY_DOWN, onKey);
|
||||
eventSource.removeEventListener(KeyboardEvent.KEY_UP, onKey);
|
||||
eventSource.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
||||
eventSource.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||
stopMouseLook();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function onMouseDown(e:MouseEvent):void {
|
||||
startMouseLook();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function onMouseUp(e:MouseEvent):void {
|
||||
stopMouseLook();
|
||||
}
|
||||
|
||||
/**
|
||||
* Включает режим взгляда мышью.
|
||||
*/
|
||||
public function startMouseLook():void {
|
||||
mousePoint.x = eventSource.mouseX;
|
||||
mousePoint.y = eventSource.mouseY;
|
||||
mouseLook = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Отключает режим взгляда мышью.
|
||||
*/
|
||||
public function stopMouseLook():void {
|
||||
mouseLook = false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function onKey(e:KeyboardEvent):void {
|
||||
var method:Function = keyBindings[e.keyCode];
|
||||
if (method != null) method.call(this, e.type == KeyboardEvent.KEY_DOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Управляемый объект.
|
||||
*/
|
||||
public function set object(value:Object3D):void {
|
||||
_object = value;
|
||||
updateObjectTransform();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function get object():Object3D {
|
||||
return _object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновляет инофрмацию о трансформации объекта. Метод следует вызывать после изменения матрицы объекта вне контролллера.
|
||||
*/
|
||||
public function updateObjectTransform():void {
|
||||
if (_object != null) objectTransform = _object.matrix.decompose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет новое положение объекта, используя внутренний счётчик времени.
|
||||
*/
|
||||
public function update():void {
|
||||
if (_object == null) return;
|
||||
|
||||
var frameTime:Number = time;
|
||||
time = getTimer();
|
||||
frameTime = 0.001*(time - frameTime);
|
||||
if (frameTime > 0.1) frameTime = 0.1;
|
||||
|
||||
var moved:Boolean = false;
|
||||
|
||||
if (mouseLook) {
|
||||
var dx:Number = eventSource.mouseX - mousePoint.x;
|
||||
var dy:Number = eventSource.mouseY - mousePoint.y;
|
||||
mousePoint.x = eventSource.mouseX;
|
||||
mousePoint.y = eventSource.mouseY;
|
||||
var v:Vector3D = objectTransform[1];
|
||||
v.x -= dy*Math.PI/180*mouseSensitivity;
|
||||
if (v.x > maxPitch) v.x = maxPitch;
|
||||
if (v.x < minPitch) v.x = minPitch;
|
||||
v.z -= dx*Math.PI/180*mouseSensitivity;
|
||||
moved = true;
|
||||
}
|
||||
|
||||
displacement.x = _right ? 1 : (_left ? -1 : 0);
|
||||
displacement.y = _forward ? 1 : (_back ? -1 : 0);
|
||||
displacement.z = _up ? 1 : (_down ? -1 : 0);
|
||||
if (displacement.lengthSquared > 0) {
|
||||
if (_object is Camera3D) {
|
||||
var tmp:Number = displacement.z;
|
||||
displacement.z = displacement.y;
|
||||
displacement.y = -tmp;
|
||||
}
|
||||
deltaTransformVector(displacement);
|
||||
if (_accelerate) displacement.scaleBy(speedMultiplier*speed*frameTime/displacement.length);
|
||||
else displacement.scaleBy(speed*frameTime/displacement.length);
|
||||
(objectTransform[0] as Vector3D).incrementBy(displacement);
|
||||
moved = true;
|
||||
}
|
||||
|
||||
if (moved) _object.matrix.recompose(objectTransform);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param pos
|
||||
*/
|
||||
public function setObjectPos(pos:Vector3D):void {
|
||||
if (_object != null) {
|
||||
var v:Vector3D = objectTransform[0];
|
||||
v.x = pos.x;
|
||||
v.y = pos.y;
|
||||
v.z = pos.z;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param pos
|
||||
*/
|
||||
public function setObjectPosXYZ(x:Number, y:Number, z:Number):void {
|
||||
if (_object != null) {
|
||||
var v:Vector3D = objectTransform[0];
|
||||
v.x = x;
|
||||
v.y = y;
|
||||
v.z = z;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param point
|
||||
*/
|
||||
public function lookAt(point:Vector3D):void {
|
||||
lookAtXYZ(point.x, point.y, point.z);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @param z
|
||||
*/
|
||||
public function lookAtXYZ(x:Number, y:Number, z:Number):void {
|
||||
if (_object == null) return;
|
||||
var v:Vector3D = objectTransform[0];
|
||||
var dx:Number = x - v.x;
|
||||
var dy:Number = y - v.y;
|
||||
var dz:Number = z - v.z;
|
||||
v = objectTransform[1];
|
||||
v.x = Math.atan2(dz, Math.sqrt(dx*dx + dy*dy));
|
||||
if (_object is Camera3D) v.x -= 0.5*Math.PI;
|
||||
v.y = 0;
|
||||
v.z = -Math.atan2(dx, dy);
|
||||
_object.matrix.recompose(objectTransform);
|
||||
}
|
||||
|
||||
private var _vin:Vector.<Number> = new Vector.<Number>(3);
|
||||
private var _vout:Vector.<Number> = new Vector.<Number>(3);
|
||||
|
||||
private function deltaTransformVector(v:Vector3D):void {
|
||||
_vin[0] = v.x;
|
||||
_vin[1] = v.y;
|
||||
_vin[2] = v.z;
|
||||
_object.matrix.transformVectors(_vin, _vout);
|
||||
var c:Vector3D = objectTransform[0];
|
||||
v.x = _vout[0] - c.x;
|
||||
v.y = _vout[1] - c.y;
|
||||
v.z = _vout[2] - c.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Активация движения вперёд.
|
||||
*
|
||||
* @param value <code>true</code> для начала движения, <code>false</code> для окончания
|
||||
*/
|
||||
public function moveForward(value:Boolean):void {
|
||||
_forward = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Активация движения назад.
|
||||
*
|
||||
* @param value <code>true</code> для начала движения, <code>false</code> для окончания
|
||||
*/
|
||||
public function moveBack(value:Boolean):void {
|
||||
_back = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Активация движения влево.
|
||||
*
|
||||
* @param value <code>true</code> для начала движения, <code>false</code> для окончания
|
||||
*/
|
||||
public function moveLeft(value:Boolean):void {
|
||||
_left = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Активация движения вправо.
|
||||
*
|
||||
* @param value <code>true</code> для начала движения, <code>false</code> для окончания
|
||||
*/
|
||||
public function moveRight(value:Boolean):void {
|
||||
_right = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Активация движения вверх.
|
||||
*
|
||||
* @param value <code>true</code> для начала движения, <code>false</code> для окончания
|
||||
*/
|
||||
public function moveUp(value:Boolean):void {
|
||||
_up = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Активация движения вниз.
|
||||
*
|
||||
* @param value <code>true</code> для начала движения, <code>false</code> для окончания
|
||||
*/
|
||||
public function moveDown(value:Boolean):void {
|
||||
_down = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Активация режима увеличенной скорости.
|
||||
*
|
||||
* @param value <code>true</code> для включения ускорения, <code>false</code> для выключения
|
||||
*/
|
||||
public function accelerate(value:Boolean):void {
|
||||
_accelerate = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод выполняет привязку клавиши к действию. Одной клавише может быть назначено только одно действие.
|
||||
*
|
||||
* @param keyCode код клавиши
|
||||
* @param action наименование действия
|
||||
*
|
||||
* @see #unbindKey()
|
||||
* @see #unbindAll()
|
||||
*/
|
||||
public function bindKey(keyCode:uint, action:String):void {
|
||||
var method:Function = actionBindings[action];
|
||||
if (method != null) keyBindings[keyCode] = method;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function bindKeys(bindings:Array):void {
|
||||
for (var i:int = 0; i < bindings.length; i += 2) bindKey(bindings[i], bindings[i + 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Очистка привязки клавиши.
|
||||
*
|
||||
* @param keyCode код клавиши
|
||||
*
|
||||
* @see #bindKey()
|
||||
* @see #unbindAll()
|
||||
*/
|
||||
public function unbindKey(keyCode:uint):void {
|
||||
delete keyBindings[keyCode];
|
||||
}
|
||||
|
||||
/**
|
||||
* Очистка привязки всех клавиш.
|
||||
*
|
||||
* @see #bindKey()
|
||||
* @see #unbindKey()
|
||||
*/
|
||||
public function unbindAll():void {
|
||||
for (var key:* in keyBindings) delete keyBindings[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод устанавливает привязки клавиш по умолчанию. Реализация по умолчанию не делает ничего.
|
||||
*
|
||||
* @see #bindKey()
|
||||
* @see #unbindKey()
|
||||
* @see #unbindAll()
|
||||
*/
|
||||
public function setDefaultBindings():void {
|
||||
bindKey(87, ACTION_FORWARD);
|
||||
bindKey(83, ACTION_BACK);
|
||||
bindKey(65, ACTION_LEFT);
|
||||
bindKey(68, ACTION_RIGHT);
|
||||
bindKey(69, ACTION_UP);
|
||||
bindKey(67, ACTION_DOWN);
|
||||
bindKey(Keyboard.SHIFT, ACTION_ACCELERATE);
|
||||
|
||||
bindKey(Keyboard.UP, ACTION_FORWARD);
|
||||
bindKey(Keyboard.DOWN, ACTION_BACK);
|
||||
bindKey(Keyboard.LEFT, ACTION_LEFT);
|
||||
bindKey(Keyboard.RIGHT, ACTION_RIGHT);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
83
Alternativa3D7/7.1/alternativa/engine3d/core/BSPNode.as
Normal file
83
Alternativa3D7/7.1/alternativa/engine3d/core/BSPNode.as
Normal file
@@ -0,0 +1,83 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class BSPNode {
|
||||
|
||||
static private var collector:Vector.<BSPNode> = new Vector.<BSPNode>();
|
||||
static private var collectorLength:int = 0;
|
||||
|
||||
// Нормаль и смещение
|
||||
public var normalX:Number;
|
||||
public var normalY:Number;
|
||||
public var normalZ:Number;
|
||||
public var offset:Number;
|
||||
|
||||
// Дочерние ноды
|
||||
public var negative:BSPNode;
|
||||
public var positive:BSPNode;
|
||||
|
||||
// Треугольники
|
||||
public var triangles:Vector.<int> = new Vector.<int>();
|
||||
public var trianglesLength:uint = 0;
|
||||
|
||||
// Полигоны
|
||||
public var polygons:Vector.<int> = new Vector.<int>();
|
||||
public var polygonsLength:uint = 0;
|
||||
|
||||
// Флаг расположения камеры
|
||||
public var cameraInfront:Boolean;
|
||||
|
||||
// Предыдущая нода в стеке (для раскрытия рекурсии)
|
||||
//public var prev:BSPNode;
|
||||
|
||||
// Для КД
|
||||
public var ax:Number;
|
||||
public var ay:Number;
|
||||
public var az:Number;
|
||||
public var abx:Number;
|
||||
public var aby:Number;
|
||||
public var abz:Number;
|
||||
public var acx:Number;
|
||||
public var acy:Number;
|
||||
public var acz:Number;
|
||||
|
||||
static alternativa3d function create():BSPNode {
|
||||
return (collectorLength > 0) ? collector[--collectorLength] : new BSPNode();
|
||||
}
|
||||
|
||||
public function create():BSPNode {
|
||||
return (collectorLength > 0) ? collector[--collectorLength] : new BSPNode();
|
||||
}
|
||||
|
||||
public function destroy():void {
|
||||
if (negative != null) {
|
||||
negative.destroy();
|
||||
negative = null;
|
||||
}
|
||||
if (positive != null) {
|
||||
positive.destroy();
|
||||
positive = null;
|
||||
}
|
||||
trianglesLength = 0;
|
||||
polygonsLength = 0;
|
||||
collector[collectorLength++] = this;
|
||||
}
|
||||
|
||||
// Добавление фрагмента в ноду
|
||||
public function addFragment(fragment:Vector.<int>, begin:int, end:int):void {
|
||||
polygons[polygonsLength++] = end - begin;
|
||||
var i:int = begin, a:int = polygons[polygonsLength++] = fragment[i++], b:int = polygons[polygonsLength++] = fragment[i++], c:int;
|
||||
while (i < end) {
|
||||
triangles[trianglesLength++] = a;
|
||||
triangles[trianglesLength++] = b;
|
||||
triangles[trianglesLength++] = c = polygons[polygonsLength++] = fragment[i++];
|
||||
b = c;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
public class BackfaceCulling {
|
||||
|
||||
/**
|
||||
* Грань рисуется с обеих сторон.
|
||||
*/
|
||||
static public const NONE:int = 0;
|
||||
/**
|
||||
* Отсечение нативными механизмами на этапе отрисовки треугольников.
|
||||
*/
|
||||
static public const NATIVE:int = 1;
|
||||
/**
|
||||
* Отсечение по предрасчитанным нормалям.
|
||||
*/
|
||||
static public const CALCULATED_NORMALS:int = 2;
|
||||
/**
|
||||
* Отсечение по динамически расчитываемым нормалям.
|
||||
*/
|
||||
static public const DYNAMIC_NORMALS:int = 3;
|
||||
|
||||
}
|
||||
}
|
||||
67
Alternativa3D7/7.1/alternativa/engine3d/core/BoundBox.as
Normal file
67
Alternativa3D7/7.1/alternativa/engine3d/core/BoundBox.as
Normal file
@@ -0,0 +1,67 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
import __AS3__.vec.Vector;
|
||||
import flash.geom.Matrix3D;
|
||||
|
||||
public class BoundBox {
|
||||
|
||||
public var minX:Number = Number.MAX_VALUE;
|
||||
public var minY:Number = Number.MAX_VALUE;
|
||||
public var minZ:Number = Number.MAX_VALUE;
|
||||
public var maxX:Number = -Number.MAX_VALUE;
|
||||
public var maxY:Number = -Number.MAX_VALUE;
|
||||
public var maxZ:Number = -Number.MAX_VALUE;
|
||||
|
||||
public function setSize(minX:Number, minY:Number, minZ:Number, maxX:Number, maxY:Number, maxZ:Number):void {
|
||||
this.minX = minX;
|
||||
this.minY = minY;
|
||||
this.minZ = minZ;
|
||||
this.maxX = maxX;
|
||||
this.maxY = maxY;
|
||||
this.maxZ = maxZ;
|
||||
}
|
||||
|
||||
public function addBoundBox(boundBox:BoundBox):void {
|
||||
minX = (boundBox.minX < minX) ? boundBox.minX : minX;
|
||||
minY = (boundBox.minY < minY) ? boundBox.minY : minY;
|
||||
minZ = (boundBox.minZ < minZ) ? boundBox.minZ : minZ;
|
||||
maxX = (boundBox.maxX > maxX) ? boundBox.maxX : maxX;
|
||||
maxY = (boundBox.maxY > maxY) ? boundBox.maxY : maxY;
|
||||
maxZ = (boundBox.maxZ > maxZ) ? boundBox.maxZ : maxZ;
|
||||
}
|
||||
|
||||
public function addPoint(x:Number, y:Number, z:Number):void {
|
||||
if (x < minX) minX = x;
|
||||
if (x > maxX) maxX = x;
|
||||
if (y < minY) minY = y;
|
||||
if (y > maxY) maxY = y;
|
||||
if (z < minZ) minZ = z;
|
||||
if (z > maxZ) maxZ = z;
|
||||
}
|
||||
|
||||
public function infinity():void {
|
||||
minX = minY = minZ = Number.MAX_VALUE;
|
||||
maxX = maxY = maxZ = -Number.MAX_VALUE;
|
||||
}
|
||||
|
||||
public function copyFrom(boundBox:BoundBox):void {
|
||||
minX = boundBox.minX;
|
||||
minY = boundBox.minY;
|
||||
minZ = boundBox.minZ;
|
||||
maxX = boundBox.maxX;
|
||||
maxY = boundBox.maxY;
|
||||
maxZ = boundBox.maxZ;
|
||||
}
|
||||
|
||||
public function clone():BoundBox {
|
||||
var clone:BoundBox = new BoundBox();
|
||||
clone.copyFrom(this);
|
||||
return clone;
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
return "BoundBox [" + minX.toFixed(2) + ", " + minY.toFixed(2) + ", " + minZ.toFixed(2) + " - " + maxX.toFixed(2) + ", " + maxY.toFixed(2) + ", " + maxZ.toFixed(2) + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
479
Alternativa3D7/7.1/alternativa/engine3d/core/Camera3D.as
Normal file
479
Alternativa3D7/7.1/alternativa/engine3d/core/Camera3D.as
Normal file
@@ -0,0 +1,479 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.display.Bitmap;
|
||||
import flash.display.BitmapData;
|
||||
import flash.display.Sprite;
|
||||
import flash.display.StageAlign;
|
||||
import flash.events.Event;
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Rectangle;
|
||||
import flash.geom.Vector3D;
|
||||
import flash.system.System;
|
||||
import flash.text.TextField;
|
||||
import flash.text.TextFieldAutoSize;
|
||||
import flash.text.TextFormat;
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.getTimer;
|
||||
import flash.utils.getDefinitionByName;
|
||||
import flash.utils.getQualifiedClassName;
|
||||
import flash.utils.getQualifiedSuperclassName;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class Camera3D extends Object3D {
|
||||
|
||||
/**
|
||||
* Вьюпорт камеры.
|
||||
*/
|
||||
public var canvas:Canvas;
|
||||
public var fov:Number = Math.PI/2;
|
||||
/**
|
||||
* Ширина вьюпорта.
|
||||
*/
|
||||
public var width:Number = 500;
|
||||
/**
|
||||
* Высота вьюпорта.
|
||||
*/
|
||||
public var height:Number = 500;
|
||||
public var farClipping:Number = 5000;
|
||||
public var farFalloff:Number = 4000;
|
||||
public var nearClipping:Number = 50;
|
||||
|
||||
// Матрица проецирования
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var projectionMatrixData:Vector.<Number> = new Vector.<Number>(16, true);
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var projectionMatrix:Matrix3D;
|
||||
// Параметры перспективы
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var viewSize:Number;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var viewSizeX:Number;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var viewSizeY:Number;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var perspectiveScaleX:Number;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var perspectiveScaleY:Number;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var invertPerspectiveScaleX:Number;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var invertPerspectiveScaleY:Number;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var focalLength:Number;
|
||||
// Перекрытия
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var occlusionPlanes:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>();
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var occlusionEdges:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>();
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var numOccluders:int;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var occludedAll:Boolean;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function get canDraw():Boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Отрисовка иерархии объектов, в которой находится камера.
|
||||
* Перед render(), если менялись параметры камеры, нужно вызвать updateProjection().
|
||||
*/
|
||||
public function render():void {
|
||||
// Расчёт матрицы перевода из рута в камеру
|
||||
cameraMatrix.identity();
|
||||
var object:Object3D = this;
|
||||
while (object._parent != null) {
|
||||
cameraMatrix.append(object.matrix);
|
||||
object = object._parent;
|
||||
}
|
||||
cameraMatrix.invert();
|
||||
cameraMatrix.appendScale(perspectiveScaleX, perspectiveScaleY, 1);
|
||||
|
||||
numOccluders = 0;
|
||||
occludedAll = false;
|
||||
|
||||
numTriangles = 0;
|
||||
|
||||
// Отрисовка
|
||||
if (object.visible && object.canDraw) {
|
||||
object.cameraMatrix.identity();
|
||||
object.cameraMatrix.prepend(cameraMatrix);
|
||||
object.cameraMatrix.prepend(object.matrix);
|
||||
if (object.cullingInCamera(this, 63) >= 0) {
|
||||
// Отрисовка объекта
|
||||
canvas.numDraws = 0;
|
||||
if (debugMode) object.debug(this, object, canvas);
|
||||
object.draw(this, object, canvas);
|
||||
// Если не нарисовалось, зачищаем рутовый канвас
|
||||
if (canvas.numDraws == 0) canvas.removeChildren(0);
|
||||
} else {
|
||||
// Если отсеклось, зачищаем рутовый канвас
|
||||
canvas.removeChildren(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* После изменения параметров fov, width, height нужно вызвать этот метод.
|
||||
*/
|
||||
public function updateProjection():void {
|
||||
// Расчёт параметров перспективы
|
||||
viewSize = Math.sqrt(width*width + height*height)*0.5;
|
||||
focalLength = viewSize/Math.tan(fov*0.5);
|
||||
viewSizeX = width*0.5;
|
||||
viewSizeY = height*0.5;
|
||||
perspectiveScaleX = focalLength/viewSizeX;
|
||||
perspectiveScaleY = focalLength/viewSizeY;
|
||||
invertPerspectiveScaleX = viewSizeX/focalLength;
|
||||
invertPerspectiveScaleY = viewSizeY/focalLength;
|
||||
|
||||
// Подготовка матрицы проецирования
|
||||
projectionMatrixData[0] = viewSizeX;
|
||||
projectionMatrixData[5] = viewSizeY;
|
||||
projectionMatrixData[10] = 1;
|
||||
projectionMatrixData[11] = 1;
|
||||
projectionMatrix = new Matrix3D(projectionMatrixData);
|
||||
}
|
||||
|
||||
/*
|
||||
// Occlusion culling
|
||||
if (numOccluders > 0) {
|
||||
for (var n:int = 0; n < numOccluders; n++) {
|
||||
var occluder:Vector.<Number> = occluders[n];
|
||||
var occlude:Boolean = true;
|
||||
occlude: for (var j:int = 0, length:int = occluder.length; j < length;) {
|
||||
var x:Number = occluder[j++], y:Number = occluder[j++], z:Number = occluder[j++]
|
||||
for (i = 0; i <= 21; i += 3) {
|
||||
if (boundBoxVertices[i]*x + boundBoxVertices[int(i + 1)]*y + boundBoxVertices[int(i + 2)]*z > 0) {
|
||||
occlude = false;
|
||||
break occlude;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (occlude) return -1;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
private static var _tmpv:Vector.<Number> = new Vector.<Number>(3);
|
||||
|
||||
/**
|
||||
* @param v
|
||||
* @param result
|
||||
*/
|
||||
public function projectGlobal(v:Vector3D, result:Vector3D):void {
|
||||
_tmpv[0] = v.x; _tmpv[1] = v.y; _tmpv[2] = v.z;
|
||||
cameraMatrix.transformVectors(_tmpv, _tmpv);
|
||||
projectionMatrix.transformVectors(_tmpv, _tmpv);
|
||||
result.z = _tmpv[2];
|
||||
result.x = _tmpv[0]/result.z;
|
||||
result.y = _tmpv[1]/result.z;
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
|
||||
// Режим отладки
|
||||
public var debugMode:Boolean = false;
|
||||
|
||||
// Список объектов дебага
|
||||
private var debugSet:Object = new Object();
|
||||
|
||||
// Добавить в дебаг
|
||||
public function addToDebug(debug:int, ... rest):void {
|
||||
if (!debugSet[debug]) debugSet[debug] = new Dictionary();
|
||||
for (var i:int = 0; i < rest.length;) debugSet[debug][rest[i++]] = true;
|
||||
}
|
||||
|
||||
// Убрать из дебага
|
||||
public function removeFromDebug(debug:int, ... rest):void {
|
||||
if (debugSet[debug]) {
|
||||
for (var i:int = 0; i < rest.length;) delete debugSet[debug][rest[i++]];
|
||||
for (var key:* in debugSet[debug]);
|
||||
if (!key) delete debugSet[debug];
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка, находится ли объект или один из классов, от которых он нследован, в дебаге
|
||||
alternativa3d function checkInDebug(object:Object3D):int {
|
||||
var res:int = 0;
|
||||
for (var debug:int = 1; debug <= 256; debug = debug << 1) {
|
||||
if (debugSet[debug]) {
|
||||
if (debugSet[debug][Object3D] || debugSet[debug][object]) {
|
||||
res |= debug;
|
||||
} else {
|
||||
var objectClass:Class = getDefinitionByName(getQualifiedClassName(object)) as Class;
|
||||
while (objectClass != Object3D) {
|
||||
if (debugSet[debug][objectClass]) {
|
||||
res |= debug;
|
||||
break;
|
||||
}
|
||||
objectClass = Class(getDefinitionByName(getQualifiedSuperclassName(objectClass)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public var diagram:Sprite = createDiagram();
|
||||
|
||||
private var fpsTextField:TextField;
|
||||
private var memoryTextField:TextField;
|
||||
private var trianglesTextField:TextField;
|
||||
private var timerTextField:TextField;
|
||||
private var graph:Bitmap;
|
||||
private var rect:Rectangle;
|
||||
|
||||
private var _diagramAlign:String = "TR";
|
||||
private var _diagramHorizontalMargin:Number = 2;
|
||||
private var _diagramVerticalMargin:Number = 2;
|
||||
|
||||
public var fpsUpdatePeriod:int = 10;
|
||||
private var fpsUpdateCounter:int;
|
||||
private var previousFrameTime:int;
|
||||
private var previousPeriodTime:int;
|
||||
|
||||
private var maxMemory:int;
|
||||
alternativa3d var numTriangles:int;
|
||||
|
||||
public var timerUpdatePeriod:int = 10;
|
||||
private var timerUpdateCounter:int;
|
||||
private var timeSum:int;
|
||||
private var timeCount:int;
|
||||
private var timer:int;
|
||||
|
||||
private function createDiagram():Sprite {
|
||||
var diagram:Sprite = new Sprite();
|
||||
diagram.mouseEnabled = false;
|
||||
diagram.mouseChildren = false;
|
||||
// Инициализация диаграммы
|
||||
diagram.addEventListener(Event.ADDED_TO_STAGE, function():void {
|
||||
// FPS
|
||||
fpsTextField = new TextField();
|
||||
fpsTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0xCCCCCC);
|
||||
fpsTextField.autoSize = TextFieldAutoSize.LEFT;
|
||||
fpsTextField.text = "FPS: " + Number(diagram.stage.frameRate).toFixed(2);
|
||||
fpsTextField.selectable = false;
|
||||
fpsTextField.x = -3;
|
||||
fpsTextField.y = -5;
|
||||
diagram.addChild(fpsTextField);
|
||||
// Память
|
||||
memoryTextField = new TextField();
|
||||
memoryTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0xCCCC00);
|
||||
memoryTextField.autoSize = TextFieldAutoSize.LEFT;
|
||||
memoryTextField.text = "MEM: " + bytesToString(System.totalMemory);
|
||||
memoryTextField.selectable = false;
|
||||
memoryTextField.x = -3;
|
||||
memoryTextField.y = 4;
|
||||
diagram.addChild(memoryTextField);
|
||||
// Треугольники
|
||||
trianglesTextField = new TextField();
|
||||
trianglesTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0xFF6600);
|
||||
trianglesTextField.autoSize = TextFieldAutoSize.LEFT;
|
||||
trianglesTextField.text = "TRI: " + 0;
|
||||
trianglesTextField.selectable = false;
|
||||
trianglesTextField.x = -2;
|
||||
trianglesTextField.y = 13;
|
||||
diagram.addChild(trianglesTextField);
|
||||
// Время выполнения метода
|
||||
timerTextField = new TextField();
|
||||
timerTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0x0066FF);
|
||||
timerTextField.autoSize = TextFieldAutoSize.LEFT;
|
||||
timerTextField.text = "TMR:";
|
||||
timerTextField.selectable = false;
|
||||
timerTextField.x = -2;
|
||||
timerTextField.y = 13 + 9;
|
||||
diagram.addChild(timerTextField);
|
||||
// График
|
||||
graph = new Bitmap(new BitmapData(60, 40, true, 0x20FFFFFF));
|
||||
rect = new Rectangle(0, 0, 1, 40)
|
||||
graph.x = 0;
|
||||
graph.y = 27 + 9;
|
||||
diagram.addChild(graph);
|
||||
// Сброс параметров
|
||||
previousFrameTime = previousPeriodTime = getTimer();
|
||||
fpsUpdateCounter = 0;
|
||||
maxMemory = 0;
|
||||
timerUpdateCounter = 0;
|
||||
timeSum = 0;
|
||||
timeCount = 0;
|
||||
// Подписка
|
||||
diagram.stage.addEventListener(Event.ENTER_FRAME, updateDiagram, false, -1000);
|
||||
diagram.stage.addEventListener(Event.RESIZE, resizeDiagram, false, -1000);
|
||||
resizeDiagram();
|
||||
});
|
||||
// Деинициализация диаграммы
|
||||
diagram.addEventListener(Event.REMOVED_FROM_STAGE, function():void {
|
||||
// Обнуление
|
||||
diagram.removeChild(fpsTextField);
|
||||
diagram.removeChild(memoryTextField);
|
||||
diagram.removeChild(trianglesTextField);
|
||||
diagram.removeChild(graph);
|
||||
fpsTextField = null;
|
||||
memoryTextField = null;
|
||||
trianglesTextField = null;
|
||||
timerTextField = null;
|
||||
graph.bitmapData.dispose();
|
||||
graph = null;
|
||||
rect = null;
|
||||
// Отписка
|
||||
diagram.stage.removeEventListener(Event.ENTER_FRAME, updateDiagram);
|
||||
diagram.stage.removeEventListener(Event.RESIZE, resizeDiagram);
|
||||
});
|
||||
return diagram;
|
||||
}
|
||||
|
||||
private function resizeDiagram(e:Event = null):void {
|
||||
if (diagram.stage != null) {
|
||||
var coord:Point = diagram.parent.globalToLocal(new Point());
|
||||
if (_diagramAlign == StageAlign.TOP_LEFT || _diagramAlign == StageAlign.LEFT || _diagramAlign == StageAlign.BOTTOM_LEFT) {
|
||||
diagram.x = Math.round(coord.x + _diagramHorizontalMargin);
|
||||
}
|
||||
if (_diagramAlign == StageAlign.TOP || _diagramAlign == StageAlign.BOTTOM) {
|
||||
diagram.x = Math.round(coord.x + diagram.stage.stageWidth/2 - graph.width/2);
|
||||
}
|
||||
if (_diagramAlign == StageAlign.TOP_RIGHT || _diagramAlign == StageAlign.RIGHT || _diagramAlign == StageAlign.BOTTOM_RIGHT) {
|
||||
diagram.x = Math.round(coord.x + diagram.stage.stageWidth - _diagramHorizontalMargin - graph.width);
|
||||
}
|
||||
if (_diagramAlign == StageAlign.TOP_LEFT || _diagramAlign == StageAlign.TOP || _diagramAlign == StageAlign.TOP_RIGHT) {
|
||||
diagram.y = Math.round(coord.y + _diagramVerticalMargin);
|
||||
}
|
||||
if (_diagramAlign == StageAlign.LEFT || _diagramAlign == StageAlign.RIGHT) {
|
||||
diagram.y = Math.round(coord.y + diagram.stage.stageHeight/2 - (graph.y + graph.height)/2);
|
||||
}
|
||||
if (_diagramAlign == StageAlign.BOTTOM_LEFT || _diagramAlign == StageAlign.BOTTOM || _diagramAlign == StageAlign.BOTTOM_RIGHT) {
|
||||
diagram.y = Math.round(coord.y + diagram.stage.stageHeight - _diagramVerticalMargin - graph.y - graph.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function updateDiagram(e:Event):void {
|
||||
var fps:Number;
|
||||
var mod:int;
|
||||
var time:int = getTimer();
|
||||
var stageFrameRate:int = diagram.stage.frameRate;
|
||||
|
||||
// FPS текст
|
||||
if (++fpsUpdateCounter == fpsUpdatePeriod) {
|
||||
fps = 1000*fpsUpdatePeriod/(time - previousPeriodTime);
|
||||
if (fps > stageFrameRate) fps = stageFrameRate;
|
||||
mod = fps*100 % 100;
|
||||
fpsTextField.text = "FPS: " + int(fps) + "." + ((mod >= 10) ? mod : ((mod > 0) ? ("0" + mod) : "00"));
|
||||
previousPeriodTime = time;
|
||||
fpsUpdateCounter = 0;
|
||||
}
|
||||
// FPS график
|
||||
fps = 1000/(time - previousFrameTime);
|
||||
if (fps > stageFrameRate) fps = stageFrameRate;
|
||||
graph.bitmapData.scroll(1, 0);
|
||||
graph.bitmapData.fillRect(rect, 0x20FFFFFF);
|
||||
graph.bitmapData.setPixel32(0, 40*(1 - fps/stageFrameRate), 0xFFCCCCCC);
|
||||
previousFrameTime = time;
|
||||
|
||||
// Память текст
|
||||
var memory:int = System.totalMemory;
|
||||
memoryTextField.text = "MEM: " + bytesToString(memory);
|
||||
// Память график
|
||||
if (memory > maxMemory) maxMemory = memory;
|
||||
graph.bitmapData.setPixel32(0, 40*(1 - memory/maxMemory), 0xFFCCCC00);
|
||||
|
||||
// Треугольники текст
|
||||
trianglesTextField.text = "TRI: " + numTriangles;
|
||||
|
||||
// Время текст
|
||||
if (++timerUpdateCounter == timerUpdatePeriod) {
|
||||
if (timeCount > 0) {
|
||||
fps = timeSum/timeCount;
|
||||
mod = fps*100 % 100;
|
||||
timerTextField.text = "TMR: " + int(fps) + "." + ((mod >= 10) ? mod : ((mod > 0) ? ("0" + mod) : "00"));
|
||||
} else {
|
||||
timerTextField.text = "TMR:";
|
||||
}
|
||||
timerUpdateCounter = 0;
|
||||
timeSum = 0;
|
||||
timeCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public function startTimer():void {
|
||||
timer = getTimer();
|
||||
}
|
||||
|
||||
public function stopTimer():void {
|
||||
timeSum += getTimer() - timer;
|
||||
timeCount++;
|
||||
}
|
||||
|
||||
public function get diagramAlign():String {
|
||||
return _diagramAlign;
|
||||
}
|
||||
public function set diagramAlign(value:String):void {
|
||||
_diagramAlign = value;
|
||||
resizeDiagram();
|
||||
}
|
||||
|
||||
public function get diagramHorizontalMargin():Number {
|
||||
return _diagramHorizontalMargin;
|
||||
}
|
||||
public function set diagramHorizontalMargin(value:Number):void {
|
||||
_diagramHorizontalMargin = value;
|
||||
resizeDiagram();
|
||||
}
|
||||
|
||||
public function get diagramVerticalMargin():Number {
|
||||
return _diagramVerticalMargin;
|
||||
}
|
||||
public function set diagramVerticalMargin(value:Number):void {
|
||||
_diagramVerticalMargin = value;
|
||||
resizeDiagram();
|
||||
}
|
||||
|
||||
private function bytesToString(bytes:int):String {
|
||||
if (bytes < 1024) return bytes + "b";
|
||||
else if (bytes < 10240) return (bytes/1024).toFixed(2) + "kb";
|
||||
else if (bytes < 102400) return (bytes/1024).toFixed(1) + "kb";
|
||||
else if (bytes < 1048576) return (bytes >> 10) + "kb";
|
||||
else if (bytes < 10485760) return (bytes/1048576).toFixed(2) + "mb";
|
||||
else if (bytes < 104857600) return (bytes/1048576).toFixed(1) + "mb";
|
||||
else return (bytes >> 20) + "mb";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
79
Alternativa3D7/7.1/alternativa/engine3d/core/Canvas.as
Normal file
79
Alternativa3D7/7.1/alternativa/engine3d/core/Canvas.as
Normal file
@@ -0,0 +1,79 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.display.Graphics;
|
||||
import flash.display.Sprite;
|
||||
import flash.geom.ColorTransform;
|
||||
import __AS3__.vec.Vector;
|
||||
import flash.display.DisplayObject;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class Canvas extends Sprite {
|
||||
|
||||
static private const defaultColorTransform:ColorTransform = new ColorTransform();
|
||||
static private const collector:Vector.<Canvas> = new Vector.<Canvas>();
|
||||
static private var collectorLength:int = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var gfx:Graphics = graphics;
|
||||
|
||||
private var modifiedGraphics:Boolean;
|
||||
private var modifiedAlpha:Boolean;
|
||||
private var modifiedBlendMode:Boolean;
|
||||
private var modifiedColorTransform:Boolean;
|
||||
private var modifiedFilters:Boolean;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _numChildren:int = 0;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var numDraws:int = 0;
|
||||
|
||||
public function Canvas() {
|
||||
mouseEnabled = false;
|
||||
mouseChildren = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function getChildCanvas(useGraphics:Boolean, useChildren:Boolean, alpha:Number = 1, blendMode:String = "normal", colorTransform:ColorTransform = null, filters:Array = null):Canvas {
|
||||
var canvas:Canvas, displayObject:DisplayObject;
|
||||
// Зачистка не канвасов
|
||||
while (_numChildren > numDraws && !((displayObject = getChildAt(_numChildren - 1 - numDraws)) is Canvas)) removeChild(displayObject), _numChildren--;
|
||||
// Получение канваса
|
||||
if (_numChildren > numDraws++) canvas = displayObject as Canvas, canvas.gfx.clear() else canvas = (collectorLength > 0) ? collector[--collectorLength] : new Canvas(), addChildAt(canvas, 0), _numChildren++;
|
||||
// Пометка о том, что в graphics будет что-то нарисовано
|
||||
canvas.modifiedGraphics = useGraphics;
|
||||
// Если не нужны дочерние канвасы
|
||||
if (canvas._numChildren > 0 && !useChildren) canvas.removeChildren(0);
|
||||
// Установка свойств
|
||||
if (alpha != 1) canvas.alpha = alpha, canvas.modifiedAlpha = true else if (canvas.modifiedAlpha) canvas.alpha = 1, canvas.modifiedAlpha = false;
|
||||
if (blendMode != "normal") canvas.blendMode = blendMode, canvas.modifiedBlendMode = true else if (canvas.modifiedBlendMode) canvas.blendMode = "normal", canvas.modifiedBlendMode = false;
|
||||
if (colorTransform != null) colorTransform.alphaMultiplier = alpha, canvas.transform.colorTransform = colorTransform, canvas.modifiedColorTransform = true else if (canvas.modifiedColorTransform) defaultColorTransform.alphaMultiplier = alpha, canvas.transform.colorTransform = defaultColorTransform, canvas.modifiedColorTransform = false;
|
||||
if (filters != null) canvas.filters = filters, canvas.modifiedFilters = true else if (canvas.modifiedFilters) canvas.filters = null, canvas.modifiedFilters = false;
|
||||
return canvas;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function removeChildren(keep:int):void {
|
||||
for (var canvas:Canvas; _numChildren > keep; _numChildren--) {
|
||||
if ((canvas = removeChildAt(0) as Canvas) != null) {
|
||||
if (canvas.modifiedGraphics) canvas.gfx.clear();
|
||||
if (canvas._numChildren > 0) canvas.removeChildren(0);
|
||||
collector[collectorLength++] = canvas;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
19
Alternativa3D7/7.1/alternativa/engine3d/core/Clipping.as
Normal file
19
Alternativa3D7/7.1/alternativa/engine3d/core/Clipping.as
Normal file
@@ -0,0 +1,19 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
public class Clipping {
|
||||
|
||||
/**
|
||||
* Объект отсекается целиком, если он полностью вне пирамиды видимости или пересекает nearClipping камеры.
|
||||
*/
|
||||
static public const OBJECT_CULLING:int = 0;
|
||||
/**
|
||||
* Грань отсекается целиком, если она полностью вне пирамиды видимости или пересекает nearClipping камеры.
|
||||
*/
|
||||
static public const FACE_CULLING:int = 1;
|
||||
/**
|
||||
* Грань подрезается пирамидой видимости камеры.
|
||||
*/
|
||||
static public const FACE_CLIPPING:int = 2;
|
||||
|
||||
}
|
||||
}
|
||||
18
Alternativa3D7/7.1/alternativa/engine3d/core/Debug.as
Normal file
18
Alternativa3D7/7.1/alternativa/engine3d/core/Debug.as
Normal file
@@ -0,0 +1,18 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
public class Debug {
|
||||
|
||||
static public const NAMES:int = 1;
|
||||
static public const AXES:int = 2;
|
||||
static public const CENTERS:int = 4;
|
||||
static public const BOUNDS:int = 8;
|
||||
|
||||
static public const EDGES:int = 16;
|
||||
static public const VERTICES:int = 32;
|
||||
static public const NORMALS:int = 64;
|
||||
|
||||
static public const NODES:int = 128;
|
||||
static public const SPLITS:int = 256;
|
||||
|
||||
}
|
||||
}
|
||||
23
Alternativa3D7/7.1/alternativa/engine3d/core/KDNode.as
Normal file
23
Alternativa3D7/7.1/alternativa/engine3d/core/KDNode.as
Normal file
@@ -0,0 +1,23 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
public final class KDNode {
|
||||
|
||||
public var axis:int; // 0 - x, 1 - y, 2 - z
|
||||
public var coord:Number;
|
||||
public var minCoord:Number;
|
||||
public var maxCoord:Number;
|
||||
|
||||
public var positive:KDNode;
|
||||
public var negative:KDNode;
|
||||
|
||||
public var boundBox:BoundBox;
|
||||
|
||||
public var objects:Vector.<Object3D>;
|
||||
public var bounds:Vector.<BoundBox>;
|
||||
|
||||
public var numObjects:int = 0;
|
||||
public var numNonOccluders:int = 0;
|
||||
}
|
||||
}
|
||||
73
Alternativa3D7/7.1/alternativa/engine3d/core/MipMap.as
Normal file
73
Alternativa3D7/7.1/alternativa/engine3d/core/MipMap.as
Normal file
@@ -0,0 +1,73 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.display.BitmapData;
|
||||
import flash.filters.ConvolutionFilter;
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Rectangle;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Объект, представляющий текстуру в виде последовательности её уменьшенных копий.
|
||||
* Каждая следующая в два раза меньше предыдущей. Последняя имеет размер 1х1 пиксел.
|
||||
* Чем дальше от камеры отрисовываемый объект, тем меньшая текстура выбирается.
|
||||
* Это позволяет получить лучший визуальный результат и большую производительность.
|
||||
*/
|
||||
public class MipMap {
|
||||
|
||||
/**
|
||||
* Мип-текстуры
|
||||
*/
|
||||
public var textures:Vector.<BitmapData> = new Vector.<BitmapData>();
|
||||
/**
|
||||
* Количество мип-текстур
|
||||
*/
|
||||
public var num:int;
|
||||
/**
|
||||
* Отношение размера пиксела текстуры к единице измерения трёхмерного пространства
|
||||
*/
|
||||
public var resolution:Number;
|
||||
|
||||
static private const filter:ConvolutionFilter = new ConvolutionFilter(2, 2, [1, 1, 1, 1], 4, 0, false, true);
|
||||
static private const point:Point = new Point();
|
||||
static private const matrix:Matrix = new Matrix();
|
||||
static private const rect:Rectangle = new Rectangle();
|
||||
public function MipMap(texture:BitmapData, resolution:Number = 1) {
|
||||
var bmp:BitmapData = new BitmapData(texture.width, texture.height, texture.transparent);
|
||||
var current:BitmapData = textures[num++] = texture;
|
||||
filter.preserveAlpha = !texture.transparent;
|
||||
var w:Number = rect.width = texture.width, h:Number = rect.height = texture.height;
|
||||
while (w > 1 && h > 1 && rect.width > 1 && rect.height > 1) {
|
||||
bmp.applyFilter(current, rect, point, filter);
|
||||
rect.width = w >> 1;
|
||||
rect.height = h >> 1;
|
||||
matrix.a = rect.width/w;
|
||||
matrix.d = rect.height/h;
|
||||
w *= 0.5;
|
||||
h *= 0.5;
|
||||
current = new BitmapData(rect.width, rect.height, texture.transparent, 0);
|
||||
current.draw(bmp, matrix, null, null, null, false);
|
||||
textures[num++] = current;
|
||||
}
|
||||
bmp.dispose();
|
||||
this.resolution = resolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение мип-уровня в зависимости от удалённости объекта от камеры
|
||||
* @param distance Z-координата объекта в пространстве камеры
|
||||
* @param camera Камера
|
||||
* @return Индекс в списке мип-текстур textures
|
||||
*/
|
||||
public function getLevel(distance:Number, camera:Camera3D):int {
|
||||
var level:int = Math.log(distance/(camera.focalLength*resolution))*1.442695040888963387;
|
||||
if (level < 0) return 0; else if (level >= num) return num - 1;
|
||||
return level;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Alternativa3D7/7.1/alternativa/engine3d/core/MipMapping.as
Normal file
18
Alternativa3D7/7.1/alternativa/engine3d/core/MipMapping.as
Normal file
@@ -0,0 +1,18 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
public class MipMapping {
|
||||
|
||||
/**
|
||||
* Нет мипмаппинга.
|
||||
*/
|
||||
static public const NONE:int = 0;
|
||||
/**
|
||||
* Мипмаппинг по удалённости объекта от камеры.
|
||||
*/
|
||||
static public const PER_OBJECT:int = 1;
|
||||
|
||||
//static public const PER_POLYGON:int = 2;
|
||||
//static public const PER_PIXEL:int = 3;
|
||||
|
||||
}
|
||||
}
|
||||
276
Alternativa3D7/7.1/alternativa/engine3d/core/Object3D.as
Normal file
276
Alternativa3D7/7.1/alternativa/engine3d/core/Object3D.as
Normal file
@@ -0,0 +1,276 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.geom.ColorTransform;
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Utils3D;
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.getQualifiedClassName;
|
||||
import flash.utils.getDefinitionByName;
|
||||
import flash.utils.getQualifiedSuperclassName;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Базовый трёхмерный объект
|
||||
*/
|
||||
public class Object3D {
|
||||
|
||||
public var name:String;
|
||||
/**
|
||||
* Матрица трансформации. Управлять трансформацией объекта можно только через это свойство
|
||||
* путём назначения новой матрицы или с помощью методов матрицы.
|
||||
*/
|
||||
public var matrix:Matrix3D = new Matrix3D();
|
||||
public var visible:Boolean = true;
|
||||
|
||||
public var alpha:Number = 1;
|
||||
public var blendMode:String = "normal";
|
||||
public var colorTransform:ColorTransform = null;
|
||||
public var filters:Array = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _parent:Object3DContainer;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _boundBox:BoundBox;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var culling:int = 0;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var cameraMatrix:Matrix3D = new Matrix3D();
|
||||
|
||||
static private const boundBoxVertices:Vector.<Number> = new Vector.<Number>(24, true);
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Может быть отрисован
|
||||
*/
|
||||
alternativa3d function get canDraw():Boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Отрисовка
|
||||
*/
|
||||
alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Дебаг
|
||||
*/
|
||||
alternativa3d function debug(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {}
|
||||
|
||||
public function get boundBox():BoundBox {
|
||||
return _boundBox;
|
||||
}
|
||||
|
||||
public function set boundBox(value:BoundBox):void {
|
||||
_boundBox = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Расчёт баунда
|
||||
* @param matrix Трансформация пространства, в системе которого расчитывается баунд.
|
||||
* Если этот параметр не указан, баунд расчитается в локальных координатах объекта.
|
||||
* @param boundBox Баунд, в который записывается результат.
|
||||
* Если этот параметр не указан, создаётся новый экземпляр.
|
||||
* @return Расчитанный баунд.
|
||||
*/
|
||||
public function calculateBoundBox(matrix:Matrix3D = null, boundBox:BoundBox = null):BoundBox {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function get parent():Object3DContainer {
|
||||
return _parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function cullingInCamera(camera:Camera3D, parentCulling:int):int {
|
||||
if (camera.occludedAll) return -1;
|
||||
culling = parentCulling;
|
||||
var i:int, infront:Boolean, behind:Boolean, boundBox:BoundBox = this.boundBox, numOccluders:int = camera.numOccluders, cull:Boolean = culling > 0 && boundBox != null, occlude:Boolean = numOccluders > 0 && boundBox != null;
|
||||
// Расчёт точек баунда в координатах камеры
|
||||
if (cull || occlude) boundBoxVertices[0] = boundBoxVertices[3] = boundBoxVertices[6] = boundBoxVertices[9] = boundBox.minX, boundBoxVertices[1] = boundBoxVertices[4] = boundBoxVertices[13] = boundBoxVertices[16] = boundBox.minY, boundBoxVertices[2] = boundBoxVertices[8] = boundBoxVertices[14] = boundBoxVertices[20] = boundBox.minZ, boundBoxVertices[12] = boundBoxVertices[15] = boundBoxVertices[18] = boundBoxVertices[21] = boundBox.maxX, boundBoxVertices[7] = boundBoxVertices[10] = boundBoxVertices[19] = boundBoxVertices[22] = boundBox.maxY, boundBoxVertices[5] = boundBoxVertices[11] = boundBoxVertices[17] = boundBoxVertices[23] = boundBox.maxZ, cameraMatrix.transformVectors(boundBoxVertices, boundBoxVertices);
|
||||
// Куллинг
|
||||
if (cull) {
|
||||
if (culling & 1) {
|
||||
for (i = 2, infront = false, behind = false; i <= 23; i += 3) {
|
||||
if (boundBoxVertices[i] > camera.nearClipping) {
|
||||
infront = true;
|
||||
if (behind) break;
|
||||
} else {
|
||||
behind = true;
|
||||
if (infront) break;
|
||||
}
|
||||
}
|
||||
if (behind) {
|
||||
if (!infront) return -1;
|
||||
} else {
|
||||
// TODO: проверка не нужна
|
||||
if (infront) culling &= 62;
|
||||
}
|
||||
}
|
||||
if (culling & 2) {
|
||||
for (i = 2, infront = false, behind = false; i <= 23; i += 3) {
|
||||
if (boundBoxVertices[i] < camera.farClipping) {
|
||||
infront = true;
|
||||
if (behind) break;
|
||||
} else {
|
||||
behind = true;
|
||||
if (infront) break;
|
||||
}
|
||||
}
|
||||
if (behind) {
|
||||
if (!infront) return -1;
|
||||
} else {
|
||||
if (infront) culling &= 61;
|
||||
}
|
||||
}
|
||||
if (culling & 4) {
|
||||
for (i = 0, infront = false, behind = false; i <= 21; i += 3) {
|
||||
if (-boundBoxVertices[i] < boundBoxVertices[int(i + 2)]) {
|
||||
infront = true;
|
||||
if (behind) break;
|
||||
} else {
|
||||
behind = true;
|
||||
if (infront) break;
|
||||
}
|
||||
}
|
||||
if (behind) {
|
||||
if (!infront) return -1;
|
||||
} else {
|
||||
if (infront) culling &= 59;
|
||||
}
|
||||
}
|
||||
if (culling & 8) {
|
||||
for (i = 0, infront = false, behind = false; i <= 21; i += 3) {
|
||||
if (boundBoxVertices[i] < boundBoxVertices[int(i + 2)]) {
|
||||
infront = true;
|
||||
if (behind) break;
|
||||
} else {
|
||||
behind = true;
|
||||
if (infront) break;
|
||||
}
|
||||
}
|
||||
if (behind) {
|
||||
if (!infront) return -1;
|
||||
} else {
|
||||
if (infront) culling &= 55;
|
||||
}
|
||||
}
|
||||
if (culling & 16) {
|
||||
for (i = 1, infront = false, behind = false; i <= 22; i += 3) {
|
||||
if (-boundBoxVertices[i] < boundBoxVertices[int(i + 1)]) {
|
||||
infront = true;
|
||||
if (behind) break;
|
||||
} else {
|
||||
behind = true;
|
||||
if (infront) break;
|
||||
}
|
||||
}
|
||||
if (behind) {
|
||||
if (!infront) return -1;
|
||||
} else {
|
||||
if (infront) culling &= 47;
|
||||
}
|
||||
}
|
||||
if (culling & 32) {
|
||||
for (i = 1, infront = false, behind = false; i <= 22; i += 3) {
|
||||
if (boundBoxVertices[i] < boundBoxVertices[int(i + 1)]) {
|
||||
infront = true;
|
||||
if (behind) break;
|
||||
} else {
|
||||
behind = true;
|
||||
if (infront) break;
|
||||
}
|
||||
}
|
||||
if (behind) {
|
||||
if (!infront) return -1;
|
||||
} else {
|
||||
if (infront) culling &= 31;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Окклюдинг
|
||||
if (occlude) {
|
||||
for (var o:int = 0; o < numOccluders; o++) {
|
||||
var planeOccluder:Vector.<Number> = camera.occlusionPlanes[o], planeOccluderLength:int = planeOccluder.length;
|
||||
for (var ni:int = 0; ni < planeOccluderLength; ni += 3) {
|
||||
var nx:Number = planeOccluder[ni], ny:Number = planeOccluder[int(ni + 1)], nz:Number = planeOccluder[int(ni + 2)];
|
||||
for (i = 0; i < 24; i += 3) if (nx*boundBoxVertices[i] + ny*boundBoxVertices[int(i + 1)] + nz*boundBoxVertices[int(i + 2)] >= 0) break;
|
||||
if (i < 24) break;
|
||||
}
|
||||
if (ni == planeOccluderLength) return -1;
|
||||
}
|
||||
}
|
||||
return culling;
|
||||
}
|
||||
|
||||
alternativa3d function drawAxes(camera:Camera3D, canvas:Canvas):void {
|
||||
|
||||
}
|
||||
|
||||
alternativa3d function drawCenter(camera:Camera3D, canvas:Canvas):void {
|
||||
|
||||
}
|
||||
|
||||
alternativa3d function drawName(camera:Camera3D, canvas:Canvas):void {
|
||||
|
||||
}
|
||||
|
||||
static private const boundBoxProjectedVertices:Vector.<Number> = new Vector.<Number>(16, true);
|
||||
static private const boundBoxUVTs:Vector.<Number> = new Vector.<Number>(24, true);
|
||||
alternativa3d function drawBoundBox(camera:Camera3D, canvas:Canvas, color:int = -1):void {
|
||||
|
||||
var boundBox:BoundBox = this.boundBox;
|
||||
if (boundBox == null) return;
|
||||
|
||||
boundBoxVertices[0] = boundBoxVertices[3] = boundBoxVertices[6] = boundBoxVertices[9] = boundBox.minX;
|
||||
boundBoxVertices[1] = boundBoxVertices[4] = boundBoxVertices[13] = boundBoxVertices[16] = boundBox.minY;
|
||||
boundBoxVertices[2] = boundBoxVertices[8] = boundBoxVertices[14] = boundBoxVertices[20] = boundBox.minZ;
|
||||
|
||||
boundBoxVertices[12] = boundBoxVertices[15] = boundBoxVertices[18] = boundBoxVertices[21] = boundBox.maxX;
|
||||
boundBoxVertices[7] = boundBoxVertices[10] = boundBoxVertices[19] = boundBoxVertices[22] = boundBox.maxY;
|
||||
boundBoxVertices[5] = boundBoxVertices[11] = boundBoxVertices[17] = boundBoxVertices[23] = boundBox.maxZ;
|
||||
|
||||
cameraMatrix.transformVectors(boundBoxVertices, boundBoxVertices);
|
||||
for (var i:int = 0; i < 8; i++) {
|
||||
if (boundBoxVertices[int(i*3 +2)] <= 0) return;
|
||||
}
|
||||
Utils3D.projectVectors(camera.projectionMatrix, boundBoxVertices, boundBoxProjectedVertices, boundBoxUVTs);
|
||||
canvas.gfx.endFill();
|
||||
canvas.gfx.lineStyle(0, (color < 0) ? ((culling > 0) ? 0xFFFF00 : 0x00FF00) : color);
|
||||
canvas.gfx.moveTo(boundBoxProjectedVertices[0], boundBoxProjectedVertices[1]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[2], boundBoxProjectedVertices[3]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[6], boundBoxProjectedVertices[7]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[4], boundBoxProjectedVertices[5]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[0], boundBoxProjectedVertices[1]);
|
||||
canvas.gfx.moveTo(boundBoxProjectedVertices[8], boundBoxProjectedVertices[9]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[10], boundBoxProjectedVertices[11]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[14], boundBoxProjectedVertices[15]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[12], boundBoxProjectedVertices[13]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[8], boundBoxProjectedVertices[9]);
|
||||
canvas.gfx.moveTo(boundBoxProjectedVertices[0], boundBoxProjectedVertices[1]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[8], boundBoxProjectedVertices[9]);
|
||||
canvas.gfx.moveTo(boundBoxProjectedVertices[2], boundBoxProjectedVertices[3]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[10], boundBoxProjectedVertices[11]);
|
||||
canvas.gfx.moveTo(boundBoxProjectedVertices[4], boundBoxProjectedVertices[5]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[12], boundBoxProjectedVertices[13]);
|
||||
canvas.gfx.moveTo(boundBoxProjectedVertices[6], boundBoxProjectedVertices[7]);
|
||||
canvas.gfx.lineTo(boundBoxProjectedVertices[14], boundBoxProjectedVertices[15]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.geom.Matrix3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Базовый контейнер трёхмерных объектов.
|
||||
* Логика контейнеров и child-parent-отношений идентична логике
|
||||
* displayObject'ов во Flash.
|
||||
*/
|
||||
public class Object3DContainer extends Object3D {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _numChildren:int = 0;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var children:Vector.<Object3D> = new Vector.<Object3D>();
|
||||
|
||||
protected var numVisibleChildren:int = 0;
|
||||
protected var visibleChildren:Vector.<Object3D> = new Vector.<Object3D>();
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function get canDraw():Boolean {
|
||||
return _numChildren > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
// Определяем видимые объекты
|
||||
numVisibleChildren = 0;
|
||||
calculateVisibleChildren(camera, object);
|
||||
// Если нет видимых дочерних объектов выходим без отрисовки
|
||||
if (numVisibleChildren == 0) return;
|
||||
// Расчёт порядка вывода видимых объектов
|
||||
calculateOrder(camera, object);
|
||||
// Отрисовка видимых объектов
|
||||
drawVisibleChildren(camera, object, parentCanvas);
|
||||
}
|
||||
|
||||
protected function calculateVisibleChildren(camera:Camera3D, object:Object3D):void {
|
||||
var i:int = 0, child:Object3D;
|
||||
while (i < _numChildren) {
|
||||
child = children[i++];
|
||||
if (child.visible && child.canDraw) {
|
||||
child.cameraMatrix.identity();
|
||||
child.cameraMatrix.prepend(object.cameraMatrix);
|
||||
child.cameraMatrix.prepend(child.matrix);
|
||||
if (child.cullingInCamera(camera, object.culling) >= 0) {
|
||||
visibleChildren[numVisibleChildren++] = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Подрезаем список видимых детей
|
||||
visibleChildren.length = numVisibleChildren;
|
||||
}
|
||||
|
||||
protected function calculateOrder(camera:Camera3D, object:Object3D):void {}
|
||||
|
||||
// Отрисовка сзади детей
|
||||
protected function drawBack(camera:Camera3D, object:Object3D, canvas:Canvas):void {}
|
||||
|
||||
// Отрисовка перед детьми
|
||||
protected function drawFront(camera:Camera3D, object:Object3D, canvas:Canvas):void {}
|
||||
|
||||
// Отрисовка видимых детей
|
||||
protected function drawVisibleChildren(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
|
||||
// Подготовка канваса
|
||||
var canvas:Canvas = parentCanvas.getChildCanvas(false, true, object.alpha, object.blendMode, object.colorTransform, object.filters);
|
||||
canvas.numDraws = 0;
|
||||
|
||||
/*if (Debug.bounds) {
|
||||
object.drawBoundBox(camera, canvas.getChildCanvas(true, false, 1, "normal", null, null));
|
||||
}*/
|
||||
|
||||
// Отрисовываем перед детьми
|
||||
drawFront(camera, object, canvas);
|
||||
|
||||
// Отрисовываем видимые дочерние объекты от ближних к дальним
|
||||
for (var i:int = numVisibleChildren - 1; i >= 0; i--) {
|
||||
var child:Object3D = visibleChildren[i];
|
||||
if (camera.debugMode) child.debug(camera, child, canvas);
|
||||
child.draw(camera, child, canvas);
|
||||
}
|
||||
|
||||
// Отрисовываем после детей
|
||||
drawBack(camera, object, canvas);
|
||||
|
||||
// Если не было отрисовки
|
||||
if (canvas.numDraws == 0) {
|
||||
parentCanvas.numDraws--;
|
||||
return; //??????
|
||||
}
|
||||
|
||||
// Зачищаем остатки
|
||||
canvas.removeChildren(canvas.numDraws);
|
||||
}
|
||||
|
||||
override public function calculateBoundBox(matrix:Matrix3D = null, boundBox:BoundBox = null):BoundBox {
|
||||
var m:Matrix3D = matrix != null ? matrix.clone() : new Matrix3D();
|
||||
// Если указан баунд-бокс
|
||||
if (boundBox != null) {
|
||||
boundBox.infinity();
|
||||
} else {
|
||||
boundBox = new BoundBox();
|
||||
}
|
||||
// Расчитываем баунды объектов
|
||||
var childBoundBox:BoundBox = new BoundBox();
|
||||
var childMatrix:Matrix3D = new Matrix3D();
|
||||
for (var i:int = 0; i < _numChildren; i++) {
|
||||
var child:Object3D = children[i];
|
||||
childMatrix.identity();
|
||||
childMatrix.prepend(m);
|
||||
childMatrix.prepend(child.matrix);
|
||||
child.calculateBoundBox(childMatrix, childBoundBox);
|
||||
boundBox.minX = (childBoundBox.minX < boundBox.minX) ? childBoundBox.minX : boundBox.minX;
|
||||
boundBox.minY = (childBoundBox.minY < boundBox.minY) ? childBoundBox.minY : boundBox.minY;
|
||||
boundBox.minZ = (childBoundBox.minZ < boundBox.minZ) ? childBoundBox.minZ : boundBox.minZ;
|
||||
boundBox.maxX = (childBoundBox.maxX > boundBox.maxX) ? childBoundBox.maxX : boundBox.maxX;
|
||||
boundBox.maxY = (childBoundBox.maxY > boundBox.maxY) ? childBoundBox.maxY : boundBox.maxY;
|
||||
boundBox.maxZ = (childBoundBox.maxZ > boundBox.maxZ) ? childBoundBox.maxZ : boundBox.maxZ;
|
||||
}
|
||||
return boundBox;
|
||||
}
|
||||
|
||||
public function addChild(child:Object3D):void {
|
||||
children[_numChildren++] = child;
|
||||
child._parent = this;
|
||||
}
|
||||
|
||||
public function removeChild(child:Object3D):void {
|
||||
var i:int = children.indexOf(child);
|
||||
if (i < 0) throw new ArgumentError("Child not found");
|
||||
_numChildren--;
|
||||
for (; i < _numChildren; i++) children[i] = children[int(i + 1)];
|
||||
children.length = _numChildren;
|
||||
child._parent = null;
|
||||
}
|
||||
|
||||
public function hasChild(child:Object3D):Boolean {
|
||||
return children.indexOf(child) > -1;
|
||||
}
|
||||
|
||||
public function getChildAt(index:uint):Object3D {
|
||||
return children[index];
|
||||
}
|
||||
|
||||
public function get numChildren():uint {
|
||||
return _numChildren;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
19
Alternativa3D7/7.1/alternativa/engine3d/core/Sorting.as
Normal file
19
Alternativa3D7/7.1/alternativa/engine3d/core/Sorting.as
Normal file
@@ -0,0 +1,19 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
public class Sorting {
|
||||
|
||||
/**
|
||||
* Грани не сортируются.
|
||||
*/
|
||||
static public const NONE:int = 0;
|
||||
/**
|
||||
* Грани сортируются по средним Z.
|
||||
*/
|
||||
static public const AVERAGE_Z:int = 1;
|
||||
/**
|
||||
* Грани находятся в BSP-дереве.
|
||||
*/
|
||||
static public const BSP:int = 2;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.engine3d.loaders.events.BatchTextureLoaderErrorEvent;
|
||||
import alternativa.engine3d.loaders.events.LoaderEvent;
|
||||
import alternativa.engine3d.loaders.events.LoaderProgressEvent;
|
||||
|
||||
import flash.display.BitmapData;
|
||||
import flash.events.ErrorEvent;
|
||||
import flash.events.Event;
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.system.LoaderContext;
|
||||
|
||||
/**
|
||||
* Событие посылается, когда начинается загрузка пакета.
|
||||
*
|
||||
* @eventType flash.events.Event.OPEN
|
||||
*/
|
||||
[Event (name="open", type="flash.events.Event")]
|
||||
/**
|
||||
* Событие посылается, когда загрузка пакета успешно завершена.
|
||||
*
|
||||
* @eventType flash.events.Event.COMPLETE
|
||||
*/
|
||||
[Event (name="complete", type="flash.events.Event")]
|
||||
/**
|
||||
* Событие посылается при возникновении ошибки загрузки.
|
||||
*
|
||||
* @eventType alternativa.engine3d.loaders.events.BatchTextureLoaderErrorEvent.LOADER_ERROR
|
||||
*/
|
||||
[Event (name="loaderError", type="alternativa.engine3d.loaders.events.BatchTextureLoaderErrorEvent")]
|
||||
/**
|
||||
* Событие посылается, когда начинается загрузка очередной текстуры.
|
||||
*
|
||||
* @eventType alternativa.engine3d.loaders.events.LoaderEvent.PART_OPEN
|
||||
*/
|
||||
[Event (name="partOpen", type="alternativa.engine3d.loaders.events.LoaderEvent")]
|
||||
/**
|
||||
* Событие посылается, когда загрузка очередной текстуры успешно завершена.
|
||||
*
|
||||
* @eventType alternativa.engine3d.loaders.events.LoaderEvent.PART_COMPLETE
|
||||
*/
|
||||
[Event (name="partComplete", type="alternativa.engine3d.loaders.events.LoaderEvent")]
|
||||
/**
|
||||
* Событие посылается для отображения прогресса загрузки.
|
||||
*
|
||||
* @eventType alternativa.engine3d.loaders.events.LoaderProgressEvent.LOADER_PROGRESS
|
||||
*/
|
||||
[Event (name="loaderProgress", type="alternativa.engine3d.loaders.events.LoaderProgressEvent")]
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Пакетный загрузчик текстур.
|
||||
*
|
||||
* При возникновении ошибки во время загрузки очередной текстуры, пакетный загрузчик заменяет соответствующую текстуру изображением-заглушкой и
|
||||
* генерирует событие ошибки пакетного загрузчика. Пользователь пакетного загрузчика в обработчике ошибки может решить, прерывать ли процесс
|
||||
* загрузки вызовом метода close() или нет.
|
||||
*/
|
||||
public class BatchTextureLoader extends EventDispatcher {
|
||||
/**
|
||||
* Текстура-заглушка для замены незагруженных текстур.
|
||||
*/
|
||||
private static var stubBitmapData:BitmapData;
|
||||
|
||||
private static const IDLE:int = 0;
|
||||
private static const LOADING:int = 1;
|
||||
|
||||
// Состояние загрузчика
|
||||
private var state:int = IDLE;
|
||||
|
||||
// Загрузчик текстур
|
||||
private var textureLoader:TextureLoader;
|
||||
// Контекст безопасности загрузчика
|
||||
private var loaderContext:LoaderContext;
|
||||
// Базовый URL файлов текстур
|
||||
private var baseURL:String;
|
||||
// Пакет с описанием текстур материалов (textureName => TextureInfo)
|
||||
private var batch:Object;
|
||||
// Список имён текстур в пакете
|
||||
private var textureNames:Vector.<String>;
|
||||
// Индекс текущего материала.
|
||||
private var textureIndex:int;
|
||||
// Общее количество загружаемых текстур
|
||||
private var numTextures:int;
|
||||
// Результирующая таблица (textureName => BitmapData)
|
||||
private var _textures:Object;
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр загрузчика.
|
||||
*/
|
||||
public function BatchTextureLoader() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Результирующая таблица битмапов. Ключами являются имена текстур, значениями -- объекты класса BitmapData.
|
||||
*/
|
||||
public function get textures():Object {
|
||||
return _textures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Прекращает текущую загрузку.
|
||||
*/
|
||||
public function close():void {
|
||||
if (state == LOADING) {
|
||||
textureLoader.close();
|
||||
cleanup();
|
||||
_textures = null;
|
||||
state = IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает ссылку на загруженный список текстур материалов.
|
||||
*/
|
||||
public function unload():void {
|
||||
_textures = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Запускает загрузку.
|
||||
*
|
||||
* @param baseURL базовый URL файлов текстур
|
||||
* @param batch описание пакета текстур -- таблица textureName => TextureInfo
|
||||
* @param loaderContext LoaderContext для загрузки
|
||||
*/
|
||||
public function load(baseURL:String, batch:Object, loaderContext:LoaderContext = null):void {
|
||||
if (baseURL == null) {
|
||||
throw ArgumentError("Parameter baseURL cannot be null");
|
||||
}
|
||||
if (batch == null) {
|
||||
throw ArgumentError("Parameter batch cannot be null");
|
||||
}
|
||||
|
||||
this.baseURL = baseURL;
|
||||
this.batch = batch;
|
||||
this.loaderContext = loaderContext;
|
||||
|
||||
if (textureLoader == null) {
|
||||
textureLoader = new TextureLoader();
|
||||
} else {
|
||||
close();
|
||||
}
|
||||
textureLoader.addEventListener(Event.OPEN, onTextureLoadingStart);
|
||||
textureLoader.addEventListener(LoaderProgressEvent.LOADER_PROGRESS, onProgress);
|
||||
textureLoader.addEventListener(Event.COMPLETE, onTextureLoadingComplete);
|
||||
textureLoader.addEventListener(IOErrorEvent.IO_ERROR, onLoadingError);
|
||||
|
||||
// Получение массива имён текстур
|
||||
textureNames = new Vector.<String>();
|
||||
for (var textureName:String in batch) {
|
||||
textureNames.push(textureName);
|
||||
}
|
||||
numTextures = textureNames.length;
|
||||
// Старт загрузки
|
||||
textureIndex = 0;
|
||||
_textures = {};
|
||||
|
||||
if (hasEventListener(Event.OPEN)) {
|
||||
dispatchEvent(new Event(Event.OPEN));
|
||||
}
|
||||
|
||||
state = LOADING;
|
||||
loadNextTexture();
|
||||
}
|
||||
|
||||
/**
|
||||
* Запускает загрузку очередной текстуры.
|
||||
*/
|
||||
private function loadNextTexture():void {
|
||||
var info:TextureInfo = batch[textureNames[textureIndex]];
|
||||
var opacityMapFileUrl:String = info.opacityMapFileName == null || info.opacityMapFileName == "" ? null : baseURL + info.opacityMapFileName;
|
||||
textureLoader.load(baseURL + info.diffuseMapFileName, opacityMapFileUrl, loaderContext);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function onTextureLoadingStart(e:Event):void {
|
||||
if (hasEventListener(LoaderEvent.PART_OPEN)) {
|
||||
dispatchEvent(new LoaderEvent(LoaderEvent.PART_OPEN, numTextures, textureIndex));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function onProgress(e:LoaderProgressEvent):void {
|
||||
if (hasEventListener(LoaderProgressEvent.LOADER_PROGRESS)) {
|
||||
var totalProgress:Number = (textureIndex + e.totalProgress)/numTextures;
|
||||
dispatchEvent(new LoaderProgressEvent(LoaderProgressEvent.LOADER_PROGRESS, numTextures, textureIndex, totalProgress, e.bytesLoaded, e.bytesTotal));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Обрабатывает завершение загрузки текстуры.
|
||||
*/
|
||||
private function onTextureLoadingComplete(e:Event):void {
|
||||
_textures[textureNames[textureIndex]] = textureLoader.bitmapData;
|
||||
tryNextTexure();
|
||||
}
|
||||
|
||||
/**
|
||||
* Обрабатывает ошибку при загрузке текстуры. Незагруженная текстура заменяется изображением-заглушкой и
|
||||
* генерируется событие ошибки пакетного загрузчика.
|
||||
*/
|
||||
private function onLoadingError(e:ErrorEvent):void {
|
||||
var textureName:String = textureNames[textureIndex];
|
||||
_textures[textureName] = getStubBitmapData();
|
||||
dispatchEvent(new BatchTextureLoaderErrorEvent(BatchTextureLoaderErrorEvent.LOADER_ERROR, textureName, e.text));
|
||||
tryNextTexure();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function tryNextTexure():void {
|
||||
// Проверка состояния необходима, т.к. оно могло измениться в результате вызова метода close() в обработчике события ошибки загрузки
|
||||
if (state == IDLE) return;
|
||||
|
||||
if (hasEventListener(LoaderEvent.PART_COMPLETE)) {
|
||||
dispatchEvent(new LoaderEvent(LoaderEvent.PART_COMPLETE, numTextures, textureIndex));
|
||||
}
|
||||
if (++textureIndex == numTextures) {
|
||||
// Загружены все текстуры, отправляется сообщение о завершении
|
||||
cleanup();
|
||||
removeEventListeners();
|
||||
state = IDLE;
|
||||
if (hasEventListener(Event.COMPLETE)) {
|
||||
dispatchEvent(new Event(Event.COMPLETE));
|
||||
}
|
||||
} else {
|
||||
loadNextTexture();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function removeEventListeners():void {
|
||||
textureLoader.removeEventListener(Event.OPEN, onTextureLoadingStart);
|
||||
textureLoader.removeEventListener(LoaderProgressEvent.LOADER_PROGRESS, onProgress);
|
||||
textureLoader.removeEventListener(Event.COMPLETE, onTextureLoadingComplete);
|
||||
textureLoader.removeEventListener(IOErrorEvent.IO_ERROR, onLoadingError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает внутренние ссылки на объекты.
|
||||
*/
|
||||
private function cleanup():void {
|
||||
loaderContext = null;
|
||||
textureNames = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод для получения текстуры-заглушки.
|
||||
*
|
||||
* @return текстура-заглушка для замещения незагруженных текстур
|
||||
*/
|
||||
private function getStubBitmapData():BitmapData {
|
||||
if (stubBitmapData == null) {
|
||||
var size:uint = 20;
|
||||
stubBitmapData = new BitmapData(size, size, false, 0);
|
||||
for (var i:uint = 0; i < size; i++) {
|
||||
for (var j:uint = 0; j < size; j += 2) {
|
||||
stubBitmapData.setPixel((i%2) ? j : (j + 1), i, 0xFF00FF);
|
||||
}
|
||||
}
|
||||
}
|
||||
return stubBitmapData;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
931
Alternativa3D7/7.1/alternativa/engine3d/loaders/Loader3DS.as
Normal file
931
Alternativa3D7/7.1/alternativa/engine3d/loaders/Loader3DS.as
Normal file
@@ -0,0 +1,931 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
import flash.display.BitmapData;
|
||||
import flash.display.BlendMode;
|
||||
import flash.events.Event;
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.events.SecurityErrorEvent;
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Orientation3D;
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Vector3D;
|
||||
import flash.net.URLLoader;
|
||||
import flash.net.URLLoaderDataFormat;
|
||||
import flash.net.URLRequest;
|
||||
import flash.system.LoaderContext;
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Endian;
|
||||
|
||||
[Event (name="complete", type="flash.events.Event")]
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Loader3DS extends EventDispatcher {
|
||||
|
||||
private static const STATE_IDLE:int = -1;
|
||||
private static const STATE_LOADING_MODEL:int = 0;
|
||||
private static const STATE_LOADING_TEXTURES:int = 1;
|
||||
|
||||
private static var stubBitmapData:BitmapData;
|
||||
|
||||
private var _content:Vector.<Mesh>;
|
||||
private var version:uint;
|
||||
private var objectDatas:Object;
|
||||
private var animationDatas:Array;
|
||||
private var materialDatas:Array;
|
||||
private var bitmaps:Array;
|
||||
|
||||
private var modelLoader:URLLoader;
|
||||
private var textureLoader:TextureLoader;
|
||||
private var data:ByteArray;
|
||||
|
||||
private var counter:int;
|
||||
private var textureMaterialNames:Array;
|
||||
private var context:LoaderContext;
|
||||
private var path:String;
|
||||
|
||||
private var loaderState:int = STATE_IDLE;
|
||||
|
||||
/**
|
||||
* Повтор текстуры при заливке для создаваемых текстурных материалов.
|
||||
*
|
||||
* @see alternativa.engine3d.materials.TextureMaterial
|
||||
*/
|
||||
public var repeat:Boolean = true;
|
||||
/**
|
||||
* Сглаживание текстур при увеличении масштаба.
|
||||
*
|
||||
* @see alternativa.engine3d.materials.TextureMaterial
|
||||
*/
|
||||
public var smooth:Boolean = false;
|
||||
/**
|
||||
* Режим наложения цвета для создаваемых текстурных материалов.
|
||||
*
|
||||
* @see alternativa.engine3d.materials.TextureMaterial
|
||||
*/
|
||||
public var blendMode:String = BlendMode.NORMAL;
|
||||
|
||||
/**
|
||||
* Коэффициент пересчёта единиц измерения модели.
|
||||
*/
|
||||
public var units:Number = 1;
|
||||
/**
|
||||
* Устанавливаемый уровень мобильности загруженных объектов.
|
||||
*/
|
||||
public var mobility:int = 0;
|
||||
|
||||
/**
|
||||
* Прекращение текущей загрузки.
|
||||
*/
|
||||
public function close():void {
|
||||
if (loaderState == STATE_LOADING_MODEL) {
|
||||
modelLoader.close();
|
||||
}
|
||||
if (loaderState == STATE_LOADING_TEXTURES) {
|
||||
textureLoader.close();
|
||||
}
|
||||
loaderState = STATE_IDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод очищает внутренние ссылки на загруженные данные чтобы сборщик мусора мог освободить занимаемую ими память. Метод не работает
|
||||
* во время загрузки.
|
||||
*/
|
||||
public function unload():void {
|
||||
if (loaderState == STATE_IDLE) {
|
||||
clean();
|
||||
}
|
||||
}
|
||||
|
||||
private function clean():void {
|
||||
_content = null;
|
||||
objectDatas = null;
|
||||
animationDatas = null;
|
||||
materialDatas = null;
|
||||
bitmaps = null;
|
||||
textureMaterialNames = null;
|
||||
}
|
||||
|
||||
public function load(url:String, context:LoaderContext = null):void {
|
||||
path = url.substring(0, url.lastIndexOf("/") + 1);
|
||||
this.context = context;
|
||||
|
||||
// Очистка
|
||||
version = 0;
|
||||
clean();
|
||||
|
||||
if (modelLoader == null) {
|
||||
modelLoader = new URLLoader();
|
||||
modelLoader.dataFormat = URLLoaderDataFormat.BINARY;
|
||||
modelLoader.addEventListener(Event.COMPLETE, on3DSLoad);
|
||||
modelLoader.addEventListener(IOErrorEvent.IO_ERROR, on3DSError);
|
||||
modelLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, on3DSError);
|
||||
} else {
|
||||
close();
|
||||
}
|
||||
|
||||
loaderState = STATE_LOADING_MODEL;
|
||||
modelLoader.load(new URLRequest(url));
|
||||
}
|
||||
|
||||
private function on3DSLoad(e:Event):void {
|
||||
loaderState = STATE_IDLE;
|
||||
data = modelLoader.data;
|
||||
data.endian = Endian.LITTLE_ENDIAN;
|
||||
parse3DSChunk(0, data.bytesAvailable);
|
||||
}
|
||||
|
||||
private function on3DSError(e:Event):void {
|
||||
loaderState = STATE_IDLE;
|
||||
dispatchEvent(e);
|
||||
}
|
||||
|
||||
private function loadBitmaps():void {
|
||||
if (textureLoader == null) {
|
||||
textureLoader = new TextureLoader();
|
||||
textureLoader.addEventListener(Event.COMPLETE, loadNextBitmap);
|
||||
textureLoader.addEventListener(IOErrorEvent.IO_ERROR, loadNextBitmap);
|
||||
}
|
||||
|
||||
// Имена материалов с диффузными текстурами собираются в массив textureMaterialNames
|
||||
bitmaps = new Array();
|
||||
textureMaterialNames = new Array();
|
||||
for each (var materialData:MaterialData in materialDatas) {
|
||||
if (materialData.diffuseMap != null) {
|
||||
textureMaterialNames.push(materialData.name);
|
||||
}
|
||||
}
|
||||
|
||||
loaderState = STATE_LOADING_TEXTURES;
|
||||
loadNextBitmap();
|
||||
}
|
||||
|
||||
private function loadNextBitmap(e:Event = null):void {
|
||||
if (e != null) {
|
||||
if (!(e is IOErrorEvent)) {
|
||||
bitmaps[textureMaterialNames[counter]] = textureLoader.bitmapData;
|
||||
} else {
|
||||
if (stubBitmapData == null) {
|
||||
var size:uint = 20;
|
||||
stubBitmapData = new BitmapData(size, size, false, 0);
|
||||
for (var i:uint = 0; i < size; i++) {
|
||||
for (var j:uint = 0; j < size; j+=2) {
|
||||
stubBitmapData.setPixel((i % 2) ? j : (j+1), i, 0xFF00FF);
|
||||
}
|
||||
}
|
||||
}
|
||||
bitmaps[textureMaterialNames[counter]] = stubBitmapData;
|
||||
}
|
||||
} else {
|
||||
counter = -1;
|
||||
}
|
||||
counter++;
|
||||
if (counter < textureMaterialNames.length) {
|
||||
var materialData:MaterialData = materialDatas[textureMaterialNames[counter]];
|
||||
textureLoader.load(path + materialData.diffuseMap.filename, materialData.opacityMap == null ? null : path + materialData.opacityMap.filename, context);
|
||||
} else {
|
||||
loaderState = STATE_IDLE;
|
||||
buildContent();
|
||||
}
|
||||
}
|
||||
|
||||
private function buildContent():void {
|
||||
var i:uint;
|
||||
var length:uint;
|
||||
|
||||
// Формируем связи объектов
|
||||
_content = new Vector.<Mesh>();
|
||||
|
||||
// Создаём материалы
|
||||
var materialData:MaterialData;
|
||||
for (var materialName:String in materialDatas) {
|
||||
materialData = materialDatas[materialName];
|
||||
var mapData:MapData = materialData.diffuseMap;
|
||||
var materialMatrix:Matrix = new Matrix();
|
||||
if (mapData != null) {
|
||||
var rot:Number = mapData.rotation*Math.PI/180;
|
||||
var rotSin:Number = Math.sin(rot);
|
||||
var rotCos:Number = Math.cos(rot);
|
||||
materialMatrix.translate(-mapData.offsetU, mapData.offsetV);
|
||||
materialMatrix.translate(-0.5, -0.5);
|
||||
materialMatrix.rotate(-rot);
|
||||
materialMatrix.scale(mapData.scaleU, mapData.scaleV);
|
||||
materialMatrix.translate(0.5, 0.5);
|
||||
}
|
||||
materialData.matrix = materialMatrix;
|
||||
}
|
||||
|
||||
// Если есть данные об анимации и иерархии объектов
|
||||
var objectName:String;
|
||||
var objectData:ObjectData;
|
||||
var mesh:Mesh;
|
||||
if (animationDatas != null) {
|
||||
if (objectDatas != null) {
|
||||
|
||||
length = animationDatas.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var animationData:AnimationData = animationDatas[i];
|
||||
objectName = animationData.objectName;
|
||||
objectData = objectDatas[objectName];
|
||||
|
||||
// Если на один объект приходится несколько данных об анимации
|
||||
if (objectData != null) {
|
||||
var nameCounter:uint = 2;
|
||||
for (var j:uint = i + 1; j < length; j++) {
|
||||
var animationData2:AnimationData = animationDatas[j];
|
||||
if (objectName == animationData2.objectName) {
|
||||
var newName:String = objectName + nameCounter;
|
||||
var newObjectData:ObjectData = new ObjectData();
|
||||
animationData2.objectName = newName;
|
||||
newObjectData.name = newName;
|
||||
if (objectData.vertices != null) {
|
||||
newObjectData.vertices = new Vector.<Vector3D>().concat(objectData.vertices);
|
||||
}
|
||||
if (objectData.uvs != null) {
|
||||
newObjectData.uvs = new new Vector.<Point>().concat(objectData.uvs);
|
||||
}
|
||||
if (objectData.matrix != null) {
|
||||
newObjectData.matrix = objectData.matrix.clone();
|
||||
}
|
||||
if (objectData.faces != null) {
|
||||
newObjectData.faces = new Array().concat(objectData.faces);
|
||||
}
|
||||
if (objectData.surfaces != null) {
|
||||
newObjectData.surfaces = objectData.surfaces.clone();
|
||||
}
|
||||
objectDatas[newName] = newObjectData;
|
||||
nameCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (objectData != null && objectData.vertices != null) {
|
||||
// Меш
|
||||
mesh = new Mesh();
|
||||
mesh.createEmptyGeometry(objectData.vertices.length, objectData.faces.length);
|
||||
animationData.object = mesh;
|
||||
buildObject(animationData);
|
||||
buildMesh(mesh, objectData, animationData);
|
||||
_content.push(mesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (objectName in objectDatas) {
|
||||
objectData = objectDatas[objectName];
|
||||
if (objectData.vertices != null) {
|
||||
// Меш
|
||||
mesh = new Mesh();
|
||||
mesh.createEmptyGeometry(objectData.vertices.length, objectData.faces.length);
|
||||
buildMesh(mesh, objectData, null);
|
||||
_content.push(mesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Рассылаем событие о завершении
|
||||
dispatchEvent(new Event(Event.COMPLETE));
|
||||
}
|
||||
|
||||
private function buildObject(animationData:AnimationData):void {
|
||||
var object:Mesh = animationData.object;
|
||||
object.name = animationData.objectName;
|
||||
var transform:Vector.<Vector3D> = new Vector.<Vector3D>(3, true);
|
||||
transform[0] = (animationData.position == null) ? new Vector3D() : new Vector3D(animationData.position.x * units, animationData.position.y * units, animationData.position.z * units);
|
||||
transform[1] = (animationData.rotation == null) ? new Vector3D() : animationData.rotation.clone();
|
||||
transform[2] = (animationData.scale == null) ? new Vector3D(1, 1, 1) : animationData.scale.clone();
|
||||
object.matrix.recompose(transform, Orientation3D.AXIS_ANGLE);
|
||||
}
|
||||
|
||||
private function buildMesh(mesh:Mesh, objectData:ObjectData, animationData:AnimationData):void {
|
||||
// Добавляем вершины
|
||||
var i:uint;
|
||||
var j:uint;
|
||||
var k:uint;
|
||||
var key:*;
|
||||
var length:uint = objectData.vertices.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var vertexData:Vector3D = objectData.vertices[i];
|
||||
var uv:Point = objectData.uvs[i];
|
||||
j = i*3;
|
||||
mesh.vertices[j] = vertexData.x;
|
||||
mesh.vertices[j + 1] = vertexData.y;
|
||||
mesh.vertices[j + 2] = vertexData.z;
|
||||
mesh.uvts[j] = uv.x;
|
||||
mesh.uvts[j + 1] = 1 - uv.y;
|
||||
k = i << 1;
|
||||
mesh.uvs[k] = uv.x;
|
||||
mesh.uvs[k + 1] = 1 - uv.y;
|
||||
}
|
||||
|
||||
// Коррекция вершин
|
||||
if (animationData != null) {
|
||||
// Инвертируем матрицу
|
||||
objectData.matrix.invert();
|
||||
|
||||
// Вычитаем точку привязки из смещения матрицы
|
||||
if (animationData.pivot != null) {
|
||||
objectData.matrix.appendTranslation(-animationData.pivot.x, -animationData.pivot.y, -animationData.pivot.z);
|
||||
}
|
||||
|
||||
// Трансформируем вершины
|
||||
objectData.matrix.transformVectors(mesh.vertices, mesh.vertices);
|
||||
}
|
||||
for (i = 0; i < mesh.numVertices*3; i++) {
|
||||
mesh.vertices[i] *= units;
|
||||
}
|
||||
|
||||
// Добавляем грани
|
||||
length = objectData.faces.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var faceData:FaceData = objectData.faces[i];
|
||||
j = i*3;
|
||||
mesh.indices[j] = faceData.a;
|
||||
mesh.indices[j + 1] = faceData.b;
|
||||
mesh.indices[j + 2] = faceData.c;
|
||||
}
|
||||
|
||||
// Добавляем поверхности
|
||||
if (objectData.surfaces != null) {
|
||||
for (var surfaceId:String in objectData.surfaces) {
|
||||
var materialData:MaterialData = materialDatas[surfaceId];
|
||||
if (materialData.diffuseMap != null) {
|
||||
mesh.texture = bitmaps[materialData.name];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private function parse3DSChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Главный
|
||||
case 0x4D4D:
|
||||
parseMainChunk(dataIndex, dataLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parse3DSChunk(index + chunkLength, length - chunkLength);
|
||||
} else {
|
||||
// Загрузка битмап
|
||||
loadBitmaps();
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMainChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Версия
|
||||
case 0x0002:
|
||||
parseVersion(dataIndex);
|
||||
break;
|
||||
// 3D-сцена
|
||||
case 0x3D3D:
|
||||
parse3DChunk(dataIndex, dataLength);
|
||||
break;
|
||||
// Анимация
|
||||
case 0xB000:
|
||||
parseAnimationChunk(dataIndex, dataLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parseMainChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseVersion(index:uint):void {
|
||||
data.position = index;
|
||||
version = data.readUnsignedInt();
|
||||
}
|
||||
|
||||
private function parse3DChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Материал
|
||||
case 0xAFFF:
|
||||
// Парсим материал
|
||||
var material:MaterialData = new MaterialData();
|
||||
parseMaterialChunk(material, dataIndex, dataLength);
|
||||
break;
|
||||
// Объект
|
||||
case 0x4000:
|
||||
// Создаём данные объекта
|
||||
var object:ObjectData = new ObjectData();
|
||||
var objectLength:uint = parseObject(object, dataIndex);
|
||||
// Парсим объект
|
||||
parseObjectChunk(object, dataIndex + objectLength, dataLength - objectLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parse3DChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMaterialChunk(material:MaterialData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Имя материала
|
||||
case 0xA000:
|
||||
parseMaterialName(material, dataIndex);
|
||||
break;
|
||||
// Ambient color
|
||||
case 0xA010:
|
||||
break;
|
||||
// Diffuse color
|
||||
case 0xA020:
|
||||
data.position = dataIndex + 6;
|
||||
material.color = (data.readUnsignedByte() << 16) + (data.readUnsignedByte() << 8) + data.readUnsignedByte();
|
||||
break;
|
||||
// Specular color
|
||||
case 0xA030:
|
||||
break;
|
||||
// Shininess percent
|
||||
case 0xA040:
|
||||
data.position = dataIndex + 6;
|
||||
material.glossiness = data.readUnsignedShort();
|
||||
break;
|
||||
// Shininess strength percent
|
||||
case 0xA041:
|
||||
data.position = dataIndex + 6;
|
||||
material.specular = data.readUnsignedShort();
|
||||
break;
|
||||
// Transperensy
|
||||
case 0xA050:
|
||||
data.position = dataIndex + 6;
|
||||
material.transparency = data.readUnsignedShort();
|
||||
break;
|
||||
// Texture map 1
|
||||
case 0xA200:
|
||||
material.diffuseMap = new MapData();
|
||||
parseMapChunk(material.name, material.diffuseMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Texture map 2
|
||||
case 0xA33A:
|
||||
break;
|
||||
// Opacity map
|
||||
case 0xA210:
|
||||
material.opacityMap = new MapData();
|
||||
parseMapChunk(material.name, material.opacityMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Bump map
|
||||
case 0xA230:
|
||||
//material.normalMap = new MapData();
|
||||
//parseMapChunk(material.normalMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Shininess map
|
||||
case 0xA33C:
|
||||
break;
|
||||
// Specular map
|
||||
case 0xA204:
|
||||
break;
|
||||
// Self-illumination map
|
||||
case 0xA33D:
|
||||
break;
|
||||
// Reflection map
|
||||
case 0xA220:
|
||||
break;
|
||||
}
|
||||
|
||||
parseMaterialChunk(material, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMaterialName(material:MaterialData, index:uint):void {
|
||||
// Создаём список материалов, если надо
|
||||
if (materialDatas == null) {
|
||||
materialDatas = new Array();
|
||||
}
|
||||
// Получаем название материала
|
||||
material.name = getString(index);
|
||||
// Помещаем данные материала в список
|
||||
materialDatas[material.name] = material;
|
||||
}
|
||||
|
||||
private function parseMapChunk(materialName:String, map:MapData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Имя файла
|
||||
case 0xA300:
|
||||
map.filename = getString(dataIndex).toLowerCase();
|
||||
break;
|
||||
// Масштаб по U
|
||||
case 0xA354:
|
||||
data.position = dataIndex;
|
||||
map.scaleU = data.readFloat();
|
||||
break;
|
||||
// Масштаб по V
|
||||
case 0xA356:
|
||||
data.position = dataIndex;
|
||||
map.scaleV = data.readFloat();
|
||||
break;
|
||||
// Смещение по U
|
||||
case 0xA358:
|
||||
data.position = dataIndex;
|
||||
map.offsetU = data.readFloat();
|
||||
break;
|
||||
// Смещение по V
|
||||
case 0xA35A:
|
||||
data.position = dataIndex;
|
||||
map.offsetV = data.readFloat();
|
||||
break;
|
||||
// Угол поворота
|
||||
case 0xA35C:
|
||||
data.position = dataIndex;
|
||||
map.rotation = data.readFloat();
|
||||
break;
|
||||
}
|
||||
|
||||
parseMapChunk(materialName, map, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseObject(object:ObjectData, index:uint):uint {
|
||||
// Создаём список объектов, если надо
|
||||
if (objectDatas == null) {
|
||||
objectDatas = new Object();
|
||||
}
|
||||
// Получаем название объекта
|
||||
object.name = getString(index);
|
||||
// Помещаем данные объекта в список
|
||||
objectDatas[object.name] = object;
|
||||
return object.name.length + 1;
|
||||
}
|
||||
|
||||
private function parseObjectChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Меш
|
||||
case 0x4100:
|
||||
parseMeshChunk(object, dataIndex, dataLength);
|
||||
break;
|
||||
// Источник света
|
||||
case 0x4600:
|
||||
break;
|
||||
// Камера
|
||||
case 0x4700:
|
||||
break;
|
||||
}
|
||||
|
||||
parseObjectChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMeshChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Вершины
|
||||
case 0x4110:
|
||||
parseVertices(object, dataIndex);
|
||||
break;
|
||||
// UV
|
||||
case 0x4140:
|
||||
parseUVs(object, dataIndex);
|
||||
break;
|
||||
// Трансформация
|
||||
case 0x4160:
|
||||
parseMatrix(object, dataIndex);
|
||||
break;
|
||||
// Грани
|
||||
case 0x4120:
|
||||
var facesLength:uint = parseFaces(object, dataIndex);
|
||||
parseFacesChunk(object, dataIndex + facesLength, dataLength - facesLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parseMeshChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseVertices(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.vertices = new Vector.<Vector3D>();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
object.vertices.push(new Vector3D(data.readFloat(), data.readFloat(), data.readFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
private function parseUVs(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.uvs = new Vector.<Point>();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
object.uvs.push(new Point(data.readFloat(), data.readFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMatrix(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
object.matrix = new Matrix3D(Vector.<Number>([
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 0,
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 0,
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 0,
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 1
|
||||
]));
|
||||
}
|
||||
|
||||
private function parseFaces(object:ObjectData, index:uint):uint {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.faces = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
var face:FaceData = new FaceData();
|
||||
face.a = data.readUnsignedShort();
|
||||
face.b = data.readUnsignedShort();
|
||||
face.c = data.readUnsignedShort();
|
||||
object.faces.push(face);
|
||||
data.position += 2; // Пропускаем флаг
|
||||
}
|
||||
return 2 + num*8;
|
||||
}
|
||||
|
||||
private function parseFacesChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Поверхности
|
||||
case 0x4130:
|
||||
parseSurface(object, dataIndex);
|
||||
break;
|
||||
// Группы сглаживания
|
||||
case 0x4150:
|
||||
break;
|
||||
}
|
||||
|
||||
parseFacesChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseSurface(object:ObjectData, index:uint):void {
|
||||
// Создаём данные поверхности
|
||||
var surface:SurfaceData = new SurfaceData();
|
||||
// Создаём список поверхностей, если надо
|
||||
if (object.surfaces == null) {
|
||||
object.surfaces = new Object();
|
||||
}
|
||||
// Получаем название материала
|
||||
surface.materialName = getString(index);
|
||||
// Помещаем данные поверхности в список
|
||||
object.surfaces[surface.materialName] = surface;
|
||||
|
||||
// Получаем грани поверхности
|
||||
data.position = index + surface.materialName.length + 1;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
surface.faces = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
surface.faces.push(data.readUnsignedShort());
|
||||
}
|
||||
}
|
||||
|
||||
private function parseAnimationChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Анимация объекта
|
||||
case 0xB001:
|
||||
case 0xB002:
|
||||
case 0xB003:
|
||||
case 0xB004:
|
||||
case 0xB005:
|
||||
case 0xB006:
|
||||
case 0xB007:
|
||||
var animation:AnimationData = new AnimationData();
|
||||
if (animationDatas == null) {
|
||||
animationDatas = new Array();
|
||||
}
|
||||
animationDatas.push(animation);
|
||||
parseObjectAnimationChunk(animation, dataIndex, dataLength);
|
||||
break;
|
||||
|
||||
// Таймлайн
|
||||
case 0xB008:
|
||||
break;
|
||||
}
|
||||
|
||||
parseAnimationChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseObjectAnimationChunk(animation:AnimationData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Идентификация объекта и его связь
|
||||
case 0xB010:
|
||||
parseObjectAnimationInfo(animation, dataIndex);
|
||||
break;
|
||||
// Точка привязки объекта (pivot)
|
||||
case 0xB013:
|
||||
parseObjectAnimationPivot(animation, dataIndex);
|
||||
break;
|
||||
// Смещение объекта относительно родителя
|
||||
case 0xB020:
|
||||
parseObjectAnimationPosition(animation, dataIndex);
|
||||
break;
|
||||
// Поворот объекта относительно родителя (angle-axis)
|
||||
case 0xB021:
|
||||
parseObjectAnimationRotation(animation, dataIndex);
|
||||
break;
|
||||
// Масштабирование объекта относительно родителя
|
||||
case 0xB022:
|
||||
parseObjectAnimationScale(animation, dataIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
parseObjectAnimationChunk(animation, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseObjectAnimationInfo(animation:AnimationData, index:uint):void {
|
||||
var name:String = getString(index);
|
||||
data.position = index + name.length + 1 + 4;
|
||||
animation.objectName = name;
|
||||
animation.parentIndex = data.readUnsignedShort();
|
||||
}
|
||||
|
||||
private function parseObjectAnimationPivot(animation:AnimationData, index:uint):void {
|
||||
data.position = index;
|
||||
animation.pivot = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
private function parseObjectAnimationPosition(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
animation.position = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
private function parseObjectAnimationRotation(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
var angle:Number = data.readFloat();
|
||||
animation.rotation = new Vector3D(-data.readFloat(), -data.readFloat(), -data.readFloat(), angle);
|
||||
}
|
||||
|
||||
private function parseObjectAnimationScale(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
animation.scale = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
/**
|
||||
* Объект-контейнер, содержащий все загруженные объекты.
|
||||
*/
|
||||
public function get content():Vector.<Mesh> {
|
||||
return _content;
|
||||
}
|
||||
|
||||
// Считываем строку заканчивающуюся на нулевой байт
|
||||
private function getString(index:uint):String {
|
||||
data.position = index;
|
||||
var charCode:uint = data.readByte();
|
||||
var res:String = "";
|
||||
while (charCode != 0) {
|
||||
res += String.fromCharCode(charCode);
|
||||
charCode = data.readByte();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private function getRotationFrom3DSAngleAxis(angle:Number, x:Number, z:Number, y:Number):Vector3D {
|
||||
var res:Vector3D = new Vector3D();
|
||||
var s:Number = Math.sin(angle);
|
||||
var c:Number = Math.cos(angle);
|
||||
var t:Number = 1 - c;
|
||||
var k:Number = x*y*t + z*s;
|
||||
var half:Number;
|
||||
if (k > 0.998) {
|
||||
half = angle/2;
|
||||
res.z = -2*Math.atan2(x*Math.sin(half), Math.cos(half));
|
||||
res.y = -Math.PI/2;
|
||||
res.x = 0;
|
||||
return res;
|
||||
}
|
||||
if (k < -0.998) {
|
||||
half = angle/2;
|
||||
res.z = 2*Math.atan2(x*Math.sin(half), Math.cos(half));
|
||||
res.y = Math.PI/2;
|
||||
res.x = 0;
|
||||
return res;
|
||||
}
|
||||
res.z = -Math.atan2(y*s - x*z*t, 1 - (y*y + z*z)*t);
|
||||
res.y = -Math.asin(x*y*t + z*s);
|
||||
res.x = -Math.atan2(x*s - y*z*t, 1 - (x*x + z*z)*t);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Vector3D;
|
||||
import __AS3__.vec.Vector;
|
||||
import flash.geom.Point;
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
class MaterialData {
|
||||
public var name:String;
|
||||
public var color:uint;
|
||||
public var specular:uint;
|
||||
public var glossiness:uint;
|
||||
public var transparency:uint;
|
||||
public var diffuseMap:MapData;
|
||||
public var opacityMap:MapData;
|
||||
//public var normalMap:MapData;
|
||||
public var matrix:Matrix;
|
||||
}
|
||||
|
||||
class MapData {
|
||||
public var filename:String;
|
||||
public var scaleU:Number = 1;
|
||||
public var scaleV:Number = 1;
|
||||
public var offsetU:Number = 0;
|
||||
public var offsetV:Number = 0;
|
||||
public var rotation:Number = 0;
|
||||
}
|
||||
|
||||
class ObjectData {
|
||||
public var name:String;
|
||||
public var vertices:Vector.<Vector3D>;
|
||||
public var uvs:Vector.<Point>;
|
||||
public var matrix:Matrix3D;
|
||||
public var faces:Array;
|
||||
public var surfaces:Object;
|
||||
}
|
||||
|
||||
class FaceData {
|
||||
public var a:uint;
|
||||
public var b:uint;
|
||||
public var c:uint;
|
||||
}
|
||||
|
||||
class SurfaceData {
|
||||
public var materialName:String;
|
||||
public var faces:Array;
|
||||
}
|
||||
|
||||
class AnimationData {
|
||||
public var objectName:String;
|
||||
public var object:Mesh;
|
||||
public var parentIndex:uint;
|
||||
public var pivot:Vector3D;
|
||||
public var position:Vector3D;
|
||||
public var rotation:Vector3D;
|
||||
public var scale:Vector3D;
|
||||
}
|
||||
@@ -0,0 +1,819 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
import flash.display.BlendMode;
|
||||
import flash.events.Event;
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Orientation3D;
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Vector3D;
|
||||
import flash.net.URLLoader;
|
||||
import flash.system.LoaderContext;
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Endian;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class Loader3DSByteArray extends EventDispatcher {
|
||||
|
||||
private var _content:Vector.<Mesh>;
|
||||
private var version:uint;
|
||||
private var objectDatas:Object;
|
||||
private var animationDatas:Array;
|
||||
private var materialDatas:Array;
|
||||
private var bitmaps:Array;
|
||||
|
||||
private var modelLoader:URLLoader;
|
||||
private var textureLoader:TextureLoader;
|
||||
alternativa3d var data:ByteArray;
|
||||
|
||||
private var counter:int;
|
||||
private var textureMaterialNames:Array;
|
||||
private var context:LoaderContext;
|
||||
private var path:String;
|
||||
|
||||
/**
|
||||
* Повтор текстуры при заливке для создаваемых текстурных материалов.
|
||||
*
|
||||
* @see alternativa.engine3d.materials.TextureMaterial
|
||||
*/
|
||||
public var repeat:Boolean = true;
|
||||
/**
|
||||
* Сглаживание текстур при увеличении масштаба.
|
||||
*
|
||||
* @see alternativa.engine3d.materials.TextureMaterial
|
||||
*/
|
||||
public var smooth:Boolean = false;
|
||||
/**
|
||||
* Режим наложения цвета для создаваемых текстурных материалов.
|
||||
*
|
||||
* @see alternativa.engine3d.materials.TextureMaterial
|
||||
*/
|
||||
public var blendMode:String = BlendMode.NORMAL;
|
||||
|
||||
/**
|
||||
* Коэффициент пересчёта единиц измерения модели.
|
||||
*/
|
||||
public var units:Number = 1;
|
||||
/**
|
||||
* Устанавливаемый уровень мобильности загруженных объектов.
|
||||
*/
|
||||
public var mobility:int = 0;
|
||||
|
||||
private function clean():void {
|
||||
_content = null;
|
||||
objectDatas = null;
|
||||
animationDatas = null;
|
||||
materialDatas = null;
|
||||
bitmaps = null;
|
||||
textureMaterialNames = null;
|
||||
}
|
||||
|
||||
public function parseByteArray(data:ByteArray):void {
|
||||
this.data = data;
|
||||
this.data.endian = Endian.LITTLE_ENDIAN;
|
||||
parse3DSChunk(0, this.data.bytesAvailable);
|
||||
}
|
||||
|
||||
private function buildContent():void {
|
||||
var i:uint;
|
||||
var length:uint;
|
||||
|
||||
// Формируем связи объектов
|
||||
_content = new Vector.<Mesh>();
|
||||
|
||||
// Создаём материалы
|
||||
var materialData:MaterialData;
|
||||
for (var materialName:String in materialDatas) {
|
||||
materialData = materialDatas[materialName];
|
||||
var mapData:MapData = materialData.diffuseMap;
|
||||
var materialMatrix:Matrix = new Matrix();
|
||||
if (mapData != null) {
|
||||
var rot:Number = mapData.rotation*Math.PI/180;
|
||||
var rotSin:Number = Math.sin(rot);
|
||||
var rotCos:Number = Math.cos(rot);
|
||||
materialMatrix.translate(-mapData.offsetU, mapData.offsetV);
|
||||
materialMatrix.translate(-0.5, -0.5);
|
||||
materialMatrix.rotate(-rot);
|
||||
materialMatrix.scale(mapData.scaleU, mapData.scaleV);
|
||||
materialMatrix.translate(0.5, 0.5);
|
||||
}
|
||||
materialData.matrix = materialMatrix;
|
||||
}
|
||||
|
||||
// Если есть данные об анимации и иерархии объектов
|
||||
var objectName:String;
|
||||
var objectData:ObjectData;
|
||||
var mesh:Mesh;
|
||||
if (animationDatas != null) {
|
||||
if (objectDatas != null) {
|
||||
|
||||
length = animationDatas.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var animationData:AnimationData = animationDatas[i];
|
||||
objectName = animationData.objectName;
|
||||
objectData = objectDatas[objectName];
|
||||
|
||||
// Если на один объект приходится несколько данных об анимации
|
||||
if (objectData != null) {
|
||||
var nameCounter:uint = 2;
|
||||
for (var j:uint = i + 1; j < length; j++) {
|
||||
var animationData2:AnimationData = animationDatas[j];
|
||||
if (objectName == animationData2.objectName) {
|
||||
var newName:String = objectName + nameCounter;
|
||||
var newObjectData:ObjectData = new ObjectData();
|
||||
animationData2.objectName = newName;
|
||||
newObjectData.name = newName;
|
||||
if (objectData.vertices != null) {
|
||||
newObjectData.vertices = new Vector.<Vector3D>().concat(objectData.vertices);
|
||||
}
|
||||
if (objectData.uvs != null) {
|
||||
newObjectData.uvs = new new Vector.<Point>().concat(objectData.uvs);
|
||||
}
|
||||
if (objectData.matrix != null) {
|
||||
newObjectData.matrix = objectData.matrix.clone();
|
||||
}
|
||||
if (objectData.faces != null) {
|
||||
newObjectData.faces = new Array().concat(objectData.faces);
|
||||
}
|
||||
if (objectData.surfaces != null) {
|
||||
newObjectData.surfaces = objectData.surfaces.clone();
|
||||
}
|
||||
objectDatas[newName] = newObjectData;
|
||||
nameCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (objectData != null && objectData.vertices != null) {
|
||||
// Меш
|
||||
mesh = new Mesh();
|
||||
mesh.createEmptyGeometry(objectData.vertices.length, objectData.faces.length);
|
||||
animationData.object = mesh;
|
||||
buildObject(animationData);
|
||||
buildMesh(mesh, objectData, animationData);
|
||||
_content.push(mesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (objectName in objectDatas) {
|
||||
objectData = objectDatas[objectName];
|
||||
if (objectData.vertices != null) {
|
||||
// Меш
|
||||
mesh = new Mesh();
|
||||
mesh.createEmptyGeometry(objectData.vertices.length, objectData.faces.length);
|
||||
buildMesh(mesh, objectData, null);
|
||||
_content.push(mesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Рассылаем событие о завершении
|
||||
dispatchEvent(new Event(Event.COMPLETE));
|
||||
}
|
||||
|
||||
private function buildObject(animationData:AnimationData):void {
|
||||
var object:Mesh = animationData.object;
|
||||
var transform:Vector.<Vector3D> = new Vector.<Vector3D>(3, true);
|
||||
transform[0] = (animationData.position == null) ? new Vector3D() : new Vector3D(animationData.position.x * units, animationData.position.y * units, animationData.position.z * units);
|
||||
transform[1] = (animationData.rotation == null) ? new Vector3D() : animationData.rotation.clone();
|
||||
transform[2] = (animationData.scale == null) ? new Vector3D(1, 1, 1) : animationData.scale.clone();
|
||||
object.matrix.recompose(transform, Orientation3D.AXIS_ANGLE);
|
||||
}
|
||||
|
||||
private function buildMesh(mesh:Mesh, objectData:ObjectData, animationData:AnimationData):void {
|
||||
|
||||
// Добавляем вершины
|
||||
var i:uint;
|
||||
var j:uint;
|
||||
var k:uint;
|
||||
var key:*;
|
||||
var length:uint = objectData.vertices.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var vertexData:Vector3D = objectData.vertices[i];
|
||||
var uv:Point = objectData.uvs[i];
|
||||
j = i*3;
|
||||
mesh.vertices[j] = vertexData.x;
|
||||
mesh.vertices[j + 1] = vertexData.y;
|
||||
mesh.vertices[j + 2] = vertexData.z;
|
||||
mesh.uvts[j] = uv.x;
|
||||
mesh.uvts[j + 1] = 1 - uv.y;
|
||||
k = i << 1;
|
||||
mesh.uvs[k] = uv.x;
|
||||
mesh.uvs[k + 1] = 1 - uv.y;
|
||||
}
|
||||
|
||||
// Коррекция вершин
|
||||
if (animationData != null) {
|
||||
// Инвертируем матрицу
|
||||
objectData.matrix.invert();
|
||||
|
||||
// Вычитаем точку привязки из смещения матрицы
|
||||
if (animationData.pivot != null) {
|
||||
objectData.matrix.appendTranslation(-animationData.pivot.x, -animationData.pivot.y, -animationData.pivot.z);
|
||||
}
|
||||
|
||||
// Трансформируем вершины
|
||||
objectData.matrix.transformVectors(mesh.vertices, mesh.vertices);
|
||||
}
|
||||
for (i = 0; i < mesh.numVertices*3; i++) {
|
||||
mesh.vertices[i] *= units;
|
||||
}
|
||||
|
||||
// Добавляем грани
|
||||
length = objectData.faces.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var faceData:FaceData = objectData.faces[i];
|
||||
j = i*3;
|
||||
mesh.indices[j] = faceData.a;
|
||||
mesh.indices[j + 1] = faceData.b;
|
||||
mesh.indices[j + 2] = faceData.c;
|
||||
}
|
||||
|
||||
// Добавляем поверхности
|
||||
/*if (objectData.surfaces != null) {
|
||||
for (var surfaceId:String in objectData.surfaces) {
|
||||
var materialData:MaterialData = materialDatas[surfaceId];
|
||||
if (materialData.diffuseMap != null) {
|
||||
mesh.texture = bitmaps[materialData.name];
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
alternativa3d function parse3DSChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Главный
|
||||
case 0x4D4D:
|
||||
parseMainChunk(dataIndex, dataLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parse3DSChunk(index + chunkLength, length - chunkLength);
|
||||
} else {
|
||||
// Загрузка битмап
|
||||
//loadBitmaps();
|
||||
buildContent(); // Без подгрузки битмап
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMainChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Версия
|
||||
case 0x0002:
|
||||
parseVersion(dataIndex);
|
||||
break;
|
||||
// 3D-сцена
|
||||
case 0x3D3D:
|
||||
parse3DChunk(dataIndex, dataLength);
|
||||
break;
|
||||
// Анимация
|
||||
case 0xB000:
|
||||
parseAnimationChunk(dataIndex, dataLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parseMainChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseVersion(index:uint):void {
|
||||
data.position = index;
|
||||
version = data.readUnsignedInt();
|
||||
}
|
||||
|
||||
private function parse3DChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Материал
|
||||
case 0xAFFF:
|
||||
// Парсим материал
|
||||
var material:MaterialData = new MaterialData();
|
||||
parseMaterialChunk(material, dataIndex, dataLength);
|
||||
break;
|
||||
// Объект
|
||||
case 0x4000:
|
||||
// Создаём данные объекта
|
||||
var object:ObjectData = new ObjectData();
|
||||
var objectLength:uint = parseObject(object, dataIndex);
|
||||
// Парсим объект
|
||||
parseObjectChunk(object, dataIndex + objectLength, dataLength - objectLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parse3DChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMaterialChunk(material:MaterialData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Имя материала
|
||||
case 0xA000:
|
||||
parseMaterialName(material, dataIndex);
|
||||
break;
|
||||
// Ambient color
|
||||
case 0xA010:
|
||||
break;
|
||||
// Diffuse color
|
||||
case 0xA020:
|
||||
data.position = dataIndex + 6;
|
||||
material.color = (data.readUnsignedByte() << 16) + (data.readUnsignedByte() << 8) + data.readUnsignedByte();
|
||||
break;
|
||||
// Specular color
|
||||
case 0xA030:
|
||||
break;
|
||||
// Shininess percent
|
||||
case 0xA040:
|
||||
data.position = dataIndex + 6;
|
||||
material.glossiness = data.readUnsignedShort();
|
||||
break;
|
||||
// Shininess strength percent
|
||||
case 0xA041:
|
||||
data.position = dataIndex + 6;
|
||||
material.specular = data.readUnsignedShort();
|
||||
break;
|
||||
// Transperensy
|
||||
case 0xA050:
|
||||
data.position = dataIndex + 6;
|
||||
material.transparency = data.readUnsignedShort();
|
||||
break;
|
||||
// Texture map 1
|
||||
case 0xA200:
|
||||
material.diffuseMap = new MapData();
|
||||
parseMapChunk(material.name, material.diffuseMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Texture map 2
|
||||
case 0xA33A:
|
||||
break;
|
||||
// Opacity map
|
||||
case 0xA210:
|
||||
material.opacityMap = new MapData();
|
||||
parseMapChunk(material.name, material.opacityMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Bump map
|
||||
case 0xA230:
|
||||
//material.normalMap = new MapData();
|
||||
//parseMapChunk(material.normalMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Shininess map
|
||||
case 0xA33C:
|
||||
break;
|
||||
// Specular map
|
||||
case 0xA204:
|
||||
break;
|
||||
// Self-illumination map
|
||||
case 0xA33D:
|
||||
break;
|
||||
// Reflection map
|
||||
case 0xA220:
|
||||
break;
|
||||
}
|
||||
|
||||
parseMaterialChunk(material, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMaterialName(material:MaterialData, index:uint):void {
|
||||
// Создаём список материалов, если надо
|
||||
if (materialDatas == null) {
|
||||
materialDatas = new Array();
|
||||
}
|
||||
// Получаем название материала
|
||||
material.name = getString(index);
|
||||
// Помещаем данные материала в список
|
||||
materialDatas[material.name] = material;
|
||||
}
|
||||
|
||||
private function parseMapChunk(materialName:String, map:MapData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Имя файла
|
||||
case 0xA300:
|
||||
map.filename = getString(dataIndex).toLowerCase();
|
||||
break;
|
||||
// Масштаб по U
|
||||
case 0xA354:
|
||||
data.position = dataIndex;
|
||||
map.scaleU = data.readFloat();
|
||||
break;
|
||||
// Масштаб по V
|
||||
case 0xA356:
|
||||
data.position = dataIndex;
|
||||
map.scaleV = data.readFloat();
|
||||
break;
|
||||
// Смещение по U
|
||||
case 0xA358:
|
||||
data.position = dataIndex;
|
||||
map.offsetU = data.readFloat();
|
||||
break;
|
||||
// Смещение по V
|
||||
case 0xA35A:
|
||||
data.position = dataIndex;
|
||||
map.offsetV = data.readFloat();
|
||||
break;
|
||||
// Угол поворота
|
||||
case 0xA35C:
|
||||
data.position = dataIndex;
|
||||
map.rotation = data.readFloat();
|
||||
break;
|
||||
}
|
||||
|
||||
parseMapChunk(materialName, map, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseObject(object:ObjectData, index:uint):uint {
|
||||
// Создаём список объектов, если надо
|
||||
if (objectDatas == null) {
|
||||
objectDatas = new Object();
|
||||
}
|
||||
// Получаем название объекта
|
||||
object.name = getString(index);
|
||||
// Помещаем данные объекта в список
|
||||
objectDatas[object.name] = object;
|
||||
return object.name.length + 1;
|
||||
}
|
||||
|
||||
private function parseObjectChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Меш
|
||||
case 0x4100:
|
||||
parseMeshChunk(object, dataIndex, dataLength);
|
||||
break;
|
||||
// Источник света
|
||||
case 0x4600:
|
||||
break;
|
||||
// Камера
|
||||
case 0x4700:
|
||||
break;
|
||||
}
|
||||
|
||||
parseObjectChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMeshChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Вершины
|
||||
case 0x4110:
|
||||
parseVertices(object, dataIndex);
|
||||
break;
|
||||
// UV
|
||||
case 0x4140:
|
||||
parseUVs(object, dataIndex);
|
||||
break;
|
||||
// Трансформация
|
||||
case 0x4160:
|
||||
parseMatrix(object, dataIndex);
|
||||
break;
|
||||
// Грани
|
||||
case 0x4120:
|
||||
var facesLength:uint = parseFaces(object, dataIndex);
|
||||
parseFacesChunk(object, dataIndex + facesLength, dataLength - facesLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parseMeshChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseVertices(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.vertices = new Vector.<Vector3D>();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
object.vertices.push(new Vector3D(data.readFloat(), data.readFloat(), data.readFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
private function parseUVs(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.uvs = new Vector.<Point>();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
object.uvs.push(new Point(data.readFloat(), data.readFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMatrix(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
object.matrix = new Matrix3D(Vector.<Number>([
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 0,
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 0,
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 0,
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 1
|
||||
]));
|
||||
}
|
||||
|
||||
private function parseFaces(object:ObjectData, index:uint):uint {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.faces = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
var face:FaceData = new FaceData();
|
||||
face.a = data.readUnsignedShort();
|
||||
face.b = data.readUnsignedShort();
|
||||
face.c = data.readUnsignedShort();
|
||||
object.faces.push(face);
|
||||
data.position += 2; // Пропускаем флаг
|
||||
}
|
||||
return 2 + num*8;
|
||||
}
|
||||
|
||||
private function parseFacesChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Поверхности
|
||||
case 0x4130:
|
||||
parseSurface(object, dataIndex);
|
||||
break;
|
||||
// Группы сглаживания
|
||||
case 0x4150:
|
||||
break;
|
||||
}
|
||||
|
||||
parseFacesChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseSurface(object:ObjectData, index:uint):void {
|
||||
// Создаём данные поверхности
|
||||
var surface:SurfaceData = new SurfaceData();
|
||||
// Создаём список поверхностей, если надо
|
||||
if (object.surfaces == null) {
|
||||
object.surfaces = new Object();
|
||||
}
|
||||
// Получаем название материала
|
||||
surface.materialName = getString(index);
|
||||
// Помещаем данные поверхности в список
|
||||
object.surfaces[surface.materialName] = surface;
|
||||
|
||||
// Получаем грани поверхности
|
||||
data.position = index + surface.materialName.length + 1;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
surface.faces = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
surface.faces.push(data.readUnsignedShort());
|
||||
}
|
||||
}
|
||||
|
||||
private function parseAnimationChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Анимация объекта
|
||||
case 0xB001:
|
||||
case 0xB002:
|
||||
case 0xB003:
|
||||
case 0xB004:
|
||||
case 0xB005:
|
||||
case 0xB006:
|
||||
case 0xB007:
|
||||
var animation:AnimationData = new AnimationData();
|
||||
if (animationDatas == null) {
|
||||
animationDatas = new Array();
|
||||
}
|
||||
animationDatas.push(animation);
|
||||
parseObjectAnimationChunk(animation, dataIndex, dataLength);
|
||||
break;
|
||||
|
||||
// Таймлайн
|
||||
case 0xB008:
|
||||
break;
|
||||
}
|
||||
|
||||
parseAnimationChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseObjectAnimationChunk(animation:AnimationData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Идентификация объекта и его связь
|
||||
case 0xB010:
|
||||
parseObjectAnimationInfo(animation, dataIndex);
|
||||
break;
|
||||
// Точка привязки объекта (pivot)
|
||||
case 0xB013:
|
||||
parseObjectAnimationPivot(animation, dataIndex);
|
||||
break;
|
||||
// Смещение объекта относительно родителя
|
||||
case 0xB020:
|
||||
parseObjectAnimationPosition(animation, dataIndex);
|
||||
break;
|
||||
// Поворот объекта относительно родителя (angle-axis)
|
||||
case 0xB021:
|
||||
parseObjectAnimationRotation(animation, dataIndex);
|
||||
break;
|
||||
// Масштабирование объекта относительно родителя
|
||||
case 0xB022:
|
||||
parseObjectAnimationScale(animation, dataIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
parseObjectAnimationChunk(animation, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseObjectAnimationInfo(animation:AnimationData, index:uint):void {
|
||||
var name:String = getString(index);
|
||||
data.position = index + name.length + 1 + 4;
|
||||
animation.objectName = name;
|
||||
animation.parentIndex = data.readUnsignedShort();
|
||||
}
|
||||
|
||||
private function parseObjectAnimationPivot(animation:AnimationData, index:uint):void {
|
||||
data.position = index;
|
||||
animation.pivot = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
private function parseObjectAnimationPosition(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
animation.position = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
private function parseObjectAnimationRotation(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
var angle:Number = data.readFloat();
|
||||
animation.rotation = new Vector3D(data.readFloat(), -data.readFloat(), data.readFloat(), angle);
|
||||
}
|
||||
|
||||
private function parseObjectAnimationScale(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
animation.scale = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
/**
|
||||
* Объект-контейнер, содержащий все загруженные объекты.
|
||||
*/
|
||||
public function get content():Vector.<Mesh> {
|
||||
return _content;
|
||||
}
|
||||
|
||||
// Считываем строку заканчивающуюся на нулевой байт
|
||||
private function getString(index:uint):String {
|
||||
data.position = index;
|
||||
var charCode:uint = data.readByte();
|
||||
var res:String = "";
|
||||
while (charCode != 0) {
|
||||
res += String.fromCharCode(charCode);
|
||||
charCode = data.readByte();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private function getRotationFrom3DSAngleAxis(angle:Number, x:Number, z:Number, y:Number):Vector3D {
|
||||
var res:Vector3D = new Vector3D();
|
||||
var s:Number = Math.sin(angle);
|
||||
var c:Number = Math.cos(angle);
|
||||
var t:Number = 1 - c;
|
||||
var k:Number = x*y*t + z*s;
|
||||
var half:Number;
|
||||
if (k > 0.998) {
|
||||
half = angle/2;
|
||||
res.z = -2*Math.atan2(x*Math.sin(half), Math.cos(half));
|
||||
res.y = -Math.PI/2;
|
||||
res.x = 0;
|
||||
return res;
|
||||
}
|
||||
if (k < -0.998) {
|
||||
half = angle/2;
|
||||
res.z = 2*Math.atan2(x*Math.sin(half), Math.cos(half));
|
||||
res.y = Math.PI/2;
|
||||
res.x = 0;
|
||||
return res;
|
||||
}
|
||||
res.z = -Math.atan2(y*s - x*z*t, 1 - (y*y + z*z)*t);
|
||||
res.y = -Math.asin(x*y*t + z*s);
|
||||
res.x = -Math.atan2(x*s - y*z*t, 1 - (x*x + z*z)*t);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Vector3D;
|
||||
import __AS3__.vec.Vector;
|
||||
import flash.geom.Point;
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
class MaterialData {
|
||||
public var name:String;
|
||||
public var color:uint;
|
||||
public var specular:uint;
|
||||
public var glossiness:uint;
|
||||
public var transparency:uint;
|
||||
public var diffuseMap:MapData;
|
||||
public var opacityMap:MapData;
|
||||
//public var normalMap:MapData;
|
||||
public var matrix:Matrix;
|
||||
}
|
||||
|
||||
class MapData {
|
||||
public var filename:String;
|
||||
public var scaleU:Number = 1;
|
||||
public var scaleV:Number = 1;
|
||||
public var offsetU:Number = 0;
|
||||
public var offsetV:Number = 0;
|
||||
public var rotation:Number = 0;
|
||||
}
|
||||
|
||||
class ObjectData {
|
||||
public var name:String;
|
||||
public var vertices:Vector.<Vector3D>;
|
||||
public var uvs:Vector.<Point>;
|
||||
public var matrix:Matrix3D;
|
||||
public var faces:Array;
|
||||
public var surfaces:Object;
|
||||
}
|
||||
|
||||
class FaceData {
|
||||
public var a:uint;
|
||||
public var b:uint;
|
||||
public var c:uint;
|
||||
}
|
||||
|
||||
class SurfaceData {
|
||||
public var materialName:String;
|
||||
public var faces:Array;
|
||||
}
|
||||
|
||||
class AnimationData {
|
||||
public var objectName:String;
|
||||
public var object:Mesh;
|
||||
public var parentIndex:uint;
|
||||
public var pivot:Vector3D;
|
||||
public var position:Vector3D;
|
||||
public var rotation:Vector3D;
|
||||
public var scale:Vector3D;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
|
||||
public class MaterialParams {
|
||||
|
||||
public var color:uint;
|
||||
public var opacity:Number;
|
||||
public var diffuseMap:String;
|
||||
public var opacityMap:String;
|
||||
|
||||
public function toString():String {
|
||||
return "[MaterialParams color=" + color + ", opacity=" + opacity + ", diffuseMap=" + diffuseMap + ", opacityMap=" + opacityMap + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
public class Parsed3DSData {
|
||||
|
||||
/**
|
||||
* Список объектов в порядке их появления в 3DS-данных.
|
||||
**/
|
||||
public var objects:Vector.<Object3D>;
|
||||
/**
|
||||
* Список материалов каждого объекта. Если для объекта нет назначенных материалов, соответствующий элемент списка равен null.
|
||||
**/
|
||||
public var objectMaterials:Vector.<Vector.<String>>;
|
||||
/**
|
||||
* Список материалов 3DS-файла (materialName => MaterialParams).
|
||||
*/
|
||||
public var materials:Object;
|
||||
|
||||
}
|
||||
}
|
||||
977
Alternativa3D7/7.1/alternativa/engine3d/loaders/Parser3DS.as
Normal file
977
Alternativa3D7/7.1/alternativa/engine3d/loaders/Parser3DS.as
Normal file
@@ -0,0 +1,977 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
import flash.display.BlendMode;
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Orientation3D;
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Vector3D;
|
||||
import flash.net.URLLoader;
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Endian;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Parser3DS extends EventDispatcher {
|
||||
|
||||
private var version:uint;
|
||||
private var objectDatas:Object;
|
||||
private var animationDatas:Vector.<AnimationData>;
|
||||
private var materialDatas:Object;
|
||||
|
||||
private var modelLoader:URLLoader;
|
||||
private var data:ByteArray;
|
||||
|
||||
/**
|
||||
* Повтор текстуры при заливке для создаваемых текстурных материалов.
|
||||
*
|
||||
* @see alternativa.engine3d.materials.TextureMaterial
|
||||
*/
|
||||
public var repeat:Boolean = true;
|
||||
/**
|
||||
* Сглаживание текстур при увеличении масштаба.
|
||||
*
|
||||
* @see alternativa.engine3d.materials.TextureMaterial
|
||||
*/
|
||||
public var smooth:Boolean = false;
|
||||
/**
|
||||
* Режим наложения цвета для создаваемых текстурных материалов.
|
||||
*
|
||||
* @see alternativa.engine3d.materials.TextureMaterial
|
||||
*/
|
||||
public var blendMode:String = BlendMode.NORMAL;
|
||||
|
||||
/**
|
||||
* Коэффициент пересчёта единиц измерения модели.
|
||||
*/
|
||||
public var units:Number = 1;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
public function Parser3DS() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
public function parse(data:ByteArray):Parsed3DSData {
|
||||
this.data = data;
|
||||
data.endian = Endian.LITTLE_ENDIAN;
|
||||
parse3DSChunk(0, data.bytesAvailable);
|
||||
return buildContent();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function clean():void {
|
||||
objectDatas = null;
|
||||
animationDatas = null;
|
||||
materialDatas = null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function buildContent():Parsed3DSData {
|
||||
var result:Parsed3DSData = new Parsed3DSData();
|
||||
var i:uint;
|
||||
var length:uint;
|
||||
|
||||
// Формируем связи объектов
|
||||
result.objects = new Vector.<Object3D>();
|
||||
result.objectMaterials = new Vector.<Vector.<String>>();
|
||||
result.materials = {};
|
||||
|
||||
// Создаём материалы
|
||||
var materialData:MaterialData;
|
||||
for (var materialName:String in materialDatas) {
|
||||
materialData = materialDatas[materialName];
|
||||
var mapData:MapData = materialData.diffuseMap;
|
||||
var materialMatrix:Matrix = new Matrix();
|
||||
if (mapData != null) {
|
||||
var rot:Number = mapData.rotation*Math.PI/180;
|
||||
var rotSin:Number = Math.sin(rot);
|
||||
var rotCos:Number = Math.cos(rot);
|
||||
materialMatrix.translate(-mapData.offsetU, mapData.offsetV);
|
||||
materialMatrix.translate(-0.5, -0.5);
|
||||
materialMatrix.rotate(-rot);
|
||||
materialMatrix.scale(mapData.scaleU, mapData.scaleV);
|
||||
materialMatrix.translate(0.5, 0.5);
|
||||
}
|
||||
materialData.matrix = materialMatrix;
|
||||
|
||||
var mat:MaterialParams = new MaterialParams();
|
||||
mat.color = materialData.color;
|
||||
mat.opacity = 1 - 0.01*materialData.transparency;
|
||||
if (materialData.diffuseMap != null) {
|
||||
mat.diffuseMap = materialData.diffuseMap.filename;
|
||||
}
|
||||
if (materialData.opacityMap != null) {
|
||||
mat.opacityMap = materialData.opacityMap.filename;
|
||||
}
|
||||
result.materials[materialName] = mat;
|
||||
}
|
||||
|
||||
// Если есть данные об анимации и иерархии объектов
|
||||
var objectName:String;
|
||||
var objectData:ObjectData;
|
||||
var mesh:Mesh;
|
||||
if (animationDatas != null) {
|
||||
if (objectDatas != null) {
|
||||
|
||||
length = animationDatas.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var animationData:AnimationData = animationDatas[i];
|
||||
objectName = animationData.objectName;
|
||||
objectData = objectDatas[objectName];
|
||||
|
||||
// Если на один объект приходится несколько данных об анимации
|
||||
if (objectData != null) {
|
||||
var nameCounter:uint = 2;
|
||||
for (var j:uint = i + 1; j < length; j++) {
|
||||
var animationData2:AnimationData = animationDatas[j];
|
||||
if (objectName == animationData2.objectName) {
|
||||
var newName:String = objectName + nameCounter;
|
||||
var newObjectData:ObjectData = new ObjectData();
|
||||
animationData2.objectName = newName;
|
||||
newObjectData.name = newName;
|
||||
if (objectData.vertices != null) {
|
||||
newObjectData.vertices = new Vector.<Vector3D>().concat(objectData.vertices);
|
||||
}
|
||||
if (objectData.uvs != null) {
|
||||
newObjectData.uvs = new Vector.<Point>().concat(objectData.uvs);
|
||||
}
|
||||
if (objectData.matrix != null) {
|
||||
newObjectData.matrix = objectData.matrix.clone();
|
||||
}
|
||||
if (objectData.faces != null) {
|
||||
newObjectData.faces = new Array().concat(objectData.faces);
|
||||
}
|
||||
if (objectData.surfaces != null) {
|
||||
newObjectData.surfaces = objectData.surfaces.clone();
|
||||
}
|
||||
objectDatas[newName] = newObjectData;
|
||||
nameCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (objectData != null && objectData.vertices != null) {
|
||||
// Меш
|
||||
mesh = new Mesh();
|
||||
mesh.createEmptyGeometry(objectData.vertices.length, objectData.faces.length);
|
||||
animationData.object = mesh;
|
||||
buildObject(animationData);
|
||||
buildMesh(mesh, objectData, animationData, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (objectName in objectDatas) {
|
||||
objectData = objectDatas[objectName];
|
||||
if (objectData.vertices != null) {
|
||||
// Меш
|
||||
mesh = new Mesh();
|
||||
mesh.createEmptyGeometry(objectData.vertices.length, objectData.faces.length);
|
||||
buildMesh(mesh, objectData, null, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clean();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param animationData
|
||||
*/
|
||||
private function buildObject(animationData:AnimationData):void {
|
||||
var object:Mesh = animationData.object;
|
||||
var transform:Vector.<Vector3D> = new Vector.<Vector3D>(3, true);
|
||||
transform[0] = (animationData.position == null) ? new Vector3D() : new Vector3D(animationData.position.x*units, animationData.position.y*units, animationData.position.z*units);
|
||||
transform[1] = (animationData.rotation == null) ? new Vector3D() : animationData.rotation.clone();
|
||||
transform[2] = (animationData.scale == null) ? new Vector3D(1, 1, 1) : animationData.scale.clone();
|
||||
// trace("transform[2]", transform[2]);
|
||||
object.matrix.recompose(transform, Orientation3D.AXIS_ANGLE);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mesh
|
||||
* @param objectData
|
||||
* @param animationData
|
||||
*/
|
||||
private function buildMesh(mesh:Mesh, objectData:ObjectData, animationData:AnimationData, parsedData:Parsed3DSData):void {
|
||||
mesh.name = objectData.name;
|
||||
// Добавляем вершины
|
||||
var i:uint;
|
||||
var j:uint;
|
||||
var k:uint;
|
||||
var key:*;
|
||||
var length:uint = objectData.vertices.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var vertexData:Vector3D = objectData.vertices[i];
|
||||
var uv:Point = objectData.uvs[i];
|
||||
j = i*3;
|
||||
mesh.vertices[j] = vertexData.x;
|
||||
mesh.vertices[j + 1] = vertexData.y;
|
||||
mesh.vertices[j + 2] = vertexData.z;
|
||||
mesh.uvts[j] = uv.x;
|
||||
mesh.uvts[j + 1] = 1 - uv.y;
|
||||
k = i << 1;
|
||||
mesh.uvs[k] = uv.x;
|
||||
mesh.uvs[k + 1] = 1 - uv.y;
|
||||
}
|
||||
|
||||
// Коррекция вершин
|
||||
if (animationData != null) {
|
||||
// Инвертируем матрицу
|
||||
objectData.matrix.invert();
|
||||
|
||||
// Вычитаем точку привязки из смещения матрицы
|
||||
if (animationData.pivot != null) {
|
||||
objectData.matrix.appendTranslation(-animationData.pivot.x, -animationData.pivot.y, -animationData.pivot.z);
|
||||
}
|
||||
|
||||
// Трансформируем вершины
|
||||
objectData.matrix.transformVectors(mesh.vertices, mesh.vertices);
|
||||
}
|
||||
for (i = 0; i < mesh.numVertices*3; i++) {
|
||||
mesh.vertices[i] *= units;
|
||||
}
|
||||
|
||||
// Добавляем грани
|
||||
length = objectData.faces.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var faceData:FaceData = objectData.faces[i];
|
||||
j = i*3;
|
||||
mesh.indices[j] = faceData.a;
|
||||
mesh.indices[j + 1] = faceData.b;
|
||||
mesh.indices[j + 2] = faceData.c;
|
||||
}
|
||||
|
||||
parsedData.objects.push(mesh);
|
||||
|
||||
// Добавляем поверхности
|
||||
if (objectData.surfaces != null) {
|
||||
var meshMaterials:Vector.<String> = new Vector.<String>();
|
||||
for (var surfaceId:String in objectData.surfaces) {
|
||||
meshMaterials.push(surfaceId);
|
||||
// var materialData:MaterialData = materialDatas[surfaceId];
|
||||
// if (materialData.diffuseMap != null) {
|
||||
// mesh.texture = bitmaps[materialData.name];
|
||||
// }
|
||||
}
|
||||
parsedData.objectMaterials.push(meshMaterials);
|
||||
} else {
|
||||
parsedData.objectMaterials.push(null);
|
||||
}
|
||||
// trace("mesh.matrix.decompose()", mesh.matrix.decompose());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parse3DSChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Главный
|
||||
case 0x4D4D:
|
||||
parseMainChunk(dataIndex, dataLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parse3DSChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parseMainChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Версия
|
||||
case 0x0002:
|
||||
parseVersion(dataIndex);
|
||||
break;
|
||||
// 3D-сцена
|
||||
case 0x3D3D:
|
||||
parse3DChunk(dataIndex, dataLength);
|
||||
break;
|
||||
// Анимация
|
||||
case 0xB000:
|
||||
parseAnimationChunk(dataIndex, dataLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parseMainChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param index
|
||||
*/
|
||||
private function parseVersion(index:uint):void {
|
||||
data.position = index;
|
||||
version = data.readUnsignedInt();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parse3DChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Материал
|
||||
case 0xAFFF:
|
||||
// Парсим материал
|
||||
var material:MaterialData = new MaterialData();
|
||||
parseMaterialChunk(material, dataIndex, dataLength);
|
||||
break;
|
||||
// Объект
|
||||
case 0x4000:
|
||||
// Создаём данные объекта
|
||||
var object:ObjectData = new ObjectData();
|
||||
var objectLength:uint = parseObject(object, dataIndex);
|
||||
// Парсим объект
|
||||
parseObjectChunk(object, dataIndex + objectLength, dataLength - objectLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parse3DChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param material
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parseMaterialChunk(material:MaterialData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Имя материала
|
||||
case 0xA000:
|
||||
parseMaterialName(material, dataIndex);
|
||||
break;
|
||||
// Ambient color
|
||||
case 0xA010:
|
||||
break;
|
||||
// Diffuse color
|
||||
case 0xA020:
|
||||
data.position = dataIndex + 6;
|
||||
material.color = (data.readUnsignedByte() << 16) + (data.readUnsignedByte() << 8) + data.readUnsignedByte();
|
||||
break;
|
||||
// Specular color
|
||||
case 0xA030:
|
||||
break;
|
||||
// Shininess percent
|
||||
case 0xA040:
|
||||
data.position = dataIndex + 6;
|
||||
material.glossiness = data.readUnsignedShort();
|
||||
break;
|
||||
// Shininess strength percent
|
||||
case 0xA041:
|
||||
data.position = dataIndex + 6;
|
||||
material.specular = data.readUnsignedShort();
|
||||
break;
|
||||
// Transperensy
|
||||
case 0xA050:
|
||||
data.position = dataIndex + 6;
|
||||
material.transparency = data.readUnsignedShort();
|
||||
break;
|
||||
// Texture map 1
|
||||
case 0xA200:
|
||||
material.diffuseMap = new MapData();
|
||||
parseMapChunk(material.name, material.diffuseMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Texture map 2
|
||||
case 0xA33A:
|
||||
break;
|
||||
// Opacity map
|
||||
case 0xA210:
|
||||
material.opacityMap = new MapData();
|
||||
parseMapChunk(material.name, material.opacityMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Bump map
|
||||
case 0xA230:
|
||||
//material.normalMap = new MapData();
|
||||
//parseMapChunk(material.normalMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Shininess map
|
||||
case 0xA33C:
|
||||
break;
|
||||
// Specular map
|
||||
case 0xA204:
|
||||
break;
|
||||
// Self-illumination map
|
||||
case 0xA33D:
|
||||
break;
|
||||
// Reflection map
|
||||
case 0xA220:
|
||||
break;
|
||||
}
|
||||
|
||||
parseMaterialChunk(material, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param material
|
||||
* @param index
|
||||
*/
|
||||
private function parseMaterialName(material:MaterialData, index:uint):void {
|
||||
// Создаём список материалов, если надо
|
||||
if (materialDatas == null) {
|
||||
materialDatas = {};
|
||||
}
|
||||
// Получаем название материала
|
||||
material.name = getString(index);
|
||||
// Помещаем данные материала в список
|
||||
materialDatas[material.name] = material;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param materialName
|
||||
* @param map
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parseMapChunk(materialName:String, map:MapData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Имя файла
|
||||
case 0xA300:
|
||||
map.filename = getString(dataIndex).toLowerCase();
|
||||
break;
|
||||
// Масштаб по U
|
||||
case 0xA354:
|
||||
data.position = dataIndex;
|
||||
map.scaleU = data.readFloat();
|
||||
break;
|
||||
// Масштаб по V
|
||||
case 0xA356:
|
||||
data.position = dataIndex;
|
||||
map.scaleV = data.readFloat();
|
||||
break;
|
||||
// Смещение по U
|
||||
case 0xA358:
|
||||
data.position = dataIndex;
|
||||
map.offsetU = data.readFloat();
|
||||
break;
|
||||
// Смещение по V
|
||||
case 0xA35A:
|
||||
data.position = dataIndex;
|
||||
map.offsetV = data.readFloat();
|
||||
break;
|
||||
// Угол поворота
|
||||
case 0xA35C:
|
||||
data.position = dataIndex;
|
||||
map.rotation = data.readFloat();
|
||||
break;
|
||||
}
|
||||
|
||||
parseMapChunk(materialName, map, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
private function parseObject(object:ObjectData, index:uint):uint {
|
||||
// Создаём список объектов, если надо
|
||||
if (objectDatas == null) {
|
||||
objectDatas = new Object();
|
||||
}
|
||||
// Получаем название объекта
|
||||
object.name = getString(index);
|
||||
// Помещаем данные объекта в список
|
||||
objectDatas[object.name] = object;
|
||||
return object.name.length + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parseObjectChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Меш
|
||||
case 0x4100:
|
||||
parseMeshChunk(object, dataIndex, dataLength);
|
||||
break;
|
||||
// Источник света
|
||||
case 0x4600:
|
||||
break;
|
||||
// Камера
|
||||
case 0x4700:
|
||||
break;
|
||||
}
|
||||
|
||||
parseObjectChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parseMeshChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Вершины
|
||||
case 0x4110:
|
||||
parseVertices(object, dataIndex);
|
||||
break;
|
||||
// UV
|
||||
case 0x4140:
|
||||
parseUVs(object, dataIndex);
|
||||
break;
|
||||
// Трансформация
|
||||
case 0x4160:
|
||||
parseMatrix(object, dataIndex);
|
||||
break;
|
||||
// Грани
|
||||
case 0x4120:
|
||||
var facesLength:uint = parseFaces(object, dataIndex);
|
||||
parseFacesChunk(object, dataIndex + facesLength, dataLength - facesLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parseMeshChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @param index
|
||||
*/
|
||||
private function parseVertices(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.vertices = new Vector.<Vector3D>();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
object.vertices.push(new Vector3D(data.readFloat(), data.readFloat(), data.readFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @param index
|
||||
*/
|
||||
private function parseUVs(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.uvs = new Vector.<Point>();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
object.uvs.push(new Point(data.readFloat(), data.readFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @param index
|
||||
*/
|
||||
private function parseMatrix(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
object.matrix = new Matrix3D(Vector.<Number>([
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 0,
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 0,
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 0,
|
||||
data.readFloat(), data.readFloat(), data.readFloat(), 1
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
private function parseFaces(object:ObjectData, index:uint):uint {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.faces = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
var face:FaceData = new FaceData();
|
||||
face.a = data.readUnsignedShort();
|
||||
face.b = data.readUnsignedShort();
|
||||
face.c = data.readUnsignedShort();
|
||||
object.faces.push(face);
|
||||
data.position += 2; // Пропускаем флаг
|
||||
}
|
||||
return 2 + num*8;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parseFacesChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Поверхности
|
||||
case 0x4130:
|
||||
parseSurface(object, dataIndex);
|
||||
break;
|
||||
// Группы сглаживания
|
||||
case 0x4150:
|
||||
break;
|
||||
}
|
||||
|
||||
parseFacesChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param object
|
||||
* @param index
|
||||
*/
|
||||
private function parseSurface(object:ObjectData, index:uint):void {
|
||||
// Создаём данные поверхности
|
||||
var surface:SurfaceData = new SurfaceData();
|
||||
// Создаём список поверхностей, если надо
|
||||
if (object.surfaces == null) {
|
||||
object.surfaces = new Object();
|
||||
}
|
||||
// Получаем название материала
|
||||
surface.materialName = getString(index);
|
||||
// Помещаем данные поверхности в список
|
||||
object.surfaces[surface.materialName] = surface;
|
||||
|
||||
// Получаем грани поверхности
|
||||
data.position = index + surface.materialName.length + 1;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
surface.faces = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
surface.faces.push(data.readUnsignedShort());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parseAnimationChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Анимация объекта
|
||||
case 0xB001:
|
||||
case 0xB002:
|
||||
case 0xB003:
|
||||
case 0xB004:
|
||||
case 0xB005:
|
||||
case 0xB006:
|
||||
case 0xB007:
|
||||
var animation:AnimationData = new AnimationData();
|
||||
if (animationDatas == null) {
|
||||
animationDatas = new Vector.<AnimationData>();
|
||||
}
|
||||
animationDatas.push(animation);
|
||||
parseObjectAnimationChunk(animation, dataIndex, dataLength);
|
||||
break;
|
||||
|
||||
// Таймлайн
|
||||
case 0xB008:
|
||||
break;
|
||||
}
|
||||
|
||||
parseAnimationChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param animation
|
||||
* @param index
|
||||
* @param length
|
||||
*/
|
||||
private function parseObjectAnimationChunk(animation:AnimationData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Идентификация объекта и его связь
|
||||
case 0xB010:
|
||||
parseObjectAnimationInfo(animation, dataIndex);
|
||||
break;
|
||||
// Точка привязки объекта (pivot)
|
||||
case 0xB013:
|
||||
parseObjectAnimationPivot(animation, dataIndex);
|
||||
break;
|
||||
// Смещение объекта относительно родителя
|
||||
case 0xB020:
|
||||
parseObjectAnimationPosition(animation, dataIndex);
|
||||
break;
|
||||
// Поворот объекта относительно родителя (angle-axis)
|
||||
case 0xB021:
|
||||
parseObjectAnimationRotation(animation, dataIndex);
|
||||
break;
|
||||
// Масштабирование объекта относительно родителя
|
||||
case 0xB022:
|
||||
parseObjectAnimationScale(animation, dataIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
parseObjectAnimationChunk(animation, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param animation
|
||||
* @param index
|
||||
*/
|
||||
private function parseObjectAnimationInfo(animation:AnimationData, index:uint):void {
|
||||
var name:String = getString(index);
|
||||
data.position = index + name.length + 1 + 4;
|
||||
animation.objectName = name;
|
||||
animation.parentIndex = data.readUnsignedShort();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param animation
|
||||
* @param index
|
||||
*/
|
||||
private function parseObjectAnimationPivot(animation:AnimationData, index:uint):void {
|
||||
data.position = index;
|
||||
animation.pivot = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param animation
|
||||
* @param index
|
||||
*/
|
||||
private function parseObjectAnimationPosition(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
animation.position = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param animation
|
||||
* @param index
|
||||
*/
|
||||
private function parseObjectAnimationRotation(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
var angle:Number = data.readFloat();
|
||||
animation.rotation = new Vector3D(-data.readFloat(), -data.readFloat(), -data.readFloat(), angle);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param animation
|
||||
* @param index
|
||||
*/
|
||||
private function parseObjectAnimationScale(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
animation.scale = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
/**
|
||||
* Считывает строку, заканчивающуюся на нулевой байт.
|
||||
*
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
private function getString(index:uint):String {
|
||||
data.position = index;
|
||||
var charCode:uint = data.readByte();
|
||||
var res:String = "";
|
||||
while (charCode != 0) {
|
||||
res += String.fromCharCode(charCode);
|
||||
charCode = data.readByte();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param angle
|
||||
* @param x
|
||||
* @param z
|
||||
* @param y
|
||||
* @return
|
||||
*/
|
||||
private function getRotationFrom3DSAngleAxis(angle:Number, x:Number, z:Number, y:Number):Vector3D {
|
||||
var res:Vector3D = new Vector3D();
|
||||
var s:Number = Math.sin(angle);
|
||||
var c:Number = Math.cos(angle);
|
||||
var t:Number = 1 - c;
|
||||
var k:Number = x*y*t + z*s;
|
||||
var half:Number;
|
||||
if (k >= 1) {
|
||||
half = 0.5*angle;
|
||||
res.z = -2*Math.atan2(x*Math.sin(half), Math.cos(half));
|
||||
res.y = -0.5*Math.PI;
|
||||
res.x = 0;
|
||||
return res;
|
||||
}
|
||||
if (k <= -1) {
|
||||
half = 0.5*angle;
|
||||
res.z = 2*Math.atan2(x*Math.sin(half), Math.cos(half));
|
||||
res.y = 0.5*Math.PI;
|
||||
res.x = 0;
|
||||
return res;
|
||||
}
|
||||
res.z = -Math.atan2(y*s - x*z*t, 1 - (y*y + z*z)*t);
|
||||
res.y = -Math.asin(k);
|
||||
res.x = -Math.atan2(x*s - y*z*t, 1 - (x*x + z*z)*t);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Vector3D;
|
||||
import __AS3__.vec.Vector;
|
||||
import flash.geom.Point;
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
class MaterialData {
|
||||
public var name:String;
|
||||
public var color:uint;
|
||||
public var specular:uint;
|
||||
public var glossiness:uint;
|
||||
public var transparency:uint;
|
||||
public var diffuseMap:MapData;
|
||||
public var opacityMap:MapData;
|
||||
//public var normalMap:MapData;
|
||||
public var matrix:Matrix;
|
||||
}
|
||||
|
||||
class MapData {
|
||||
public var filename:String;
|
||||
public var scaleU:Number = 1;
|
||||
public var scaleV:Number = 1;
|
||||
public var offsetU:Number = 0;
|
||||
public var offsetV:Number = 0;
|
||||
public var rotation:Number = 0;
|
||||
}
|
||||
|
||||
class ObjectData {
|
||||
public var name:String;
|
||||
public var vertices:Vector.<Vector3D>;
|
||||
public var uvs:Vector.<Point>;
|
||||
public var matrix:Matrix3D;
|
||||
public var faces:Array;
|
||||
public var surfaces:Object;
|
||||
}
|
||||
|
||||
class FaceData {
|
||||
public var a:uint;
|
||||
public var b:uint;
|
||||
public var c:uint;
|
||||
}
|
||||
|
||||
class SurfaceData {
|
||||
public var materialName:String;
|
||||
public var faces:Array;
|
||||
}
|
||||
|
||||
class AnimationData {
|
||||
public var objectName:String;
|
||||
public var object:Mesh;
|
||||
public var parentIndex:uint;
|
||||
public var pivot:Vector3D;
|
||||
public var position:Vector3D;
|
||||
public var rotation:Vector3D;
|
||||
public var scale:Vector3D;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
public class TextureFilesData {
|
||||
|
||||
public var diffuseMap:String;
|
||||
public var opacityMap:String;
|
||||
|
||||
public function TextureFilesData(diffuseMap:String, opacityMap:String) {
|
||||
this.diffuseMap = diffuseMap;
|
||||
this.opacityMap = opacityMap;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
|
||||
/**
|
||||
* Структура для хранения имён файла диффузной текстуры и файла карты прозрачности.
|
||||
*/
|
||||
public class TextureInfo {
|
||||
/**
|
||||
* Имя файла диффузной текстуры.
|
||||
*/
|
||||
public var diffuseMapFileName:String;
|
||||
/**
|
||||
* Имя файла карты прозрачности.
|
||||
*/
|
||||
public var opacityMapFileName:String;
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр.
|
||||
*
|
||||
* @param diffuseMapFileName имя файла диффузной текстуры
|
||||
* @param opacityMapFileName имя файла карты прозрачности
|
||||
*/
|
||||
public function TextureInfo(diffuseMapFileName:String = null, opacityMapFileName:String = null) {
|
||||
this.diffuseMapFileName = diffuseMapFileName;
|
||||
this.opacityMapFileName = opacityMapFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт строковое представление объекта.
|
||||
*
|
||||
* @return строковое представление объекта
|
||||
*/
|
||||
public function toString():String {
|
||||
return "[TextureInfo diffuseMapFileName=" + diffuseMapFileName + ", opacityMapFileName=" + opacityMapFileName + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
268
Alternativa3D7/7.1/alternativa/engine3d/loaders/TextureLoader.as
Normal file
268
Alternativa3D7/7.1/alternativa/engine3d/loaders/TextureLoader.as
Normal file
@@ -0,0 +1,268 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
import alternativa.engine3d.loaders.events.LoaderEvent;
|
||||
import alternativa.engine3d.loaders.events.LoaderProgressEvent;
|
||||
|
||||
import flash.display.Bitmap;
|
||||
import flash.display.BitmapData;
|
||||
import flash.display.BitmapDataChannel;
|
||||
import flash.display.BlendMode;
|
||||
import flash.display.Loader;
|
||||
import flash.display.LoaderInfo;
|
||||
import flash.events.Event;
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.events.ProgressEvent;
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Point;
|
||||
import flash.net.URLRequest;
|
||||
import flash.system.LoaderContext;
|
||||
|
||||
/**
|
||||
* Событие посылается, когда начинается загрузка ресурса.
|
||||
*
|
||||
* @eventType flash.events.Event.OPEN
|
||||
*/
|
||||
[Event (name="open", type="flash.events.Event")]
|
||||
/**
|
||||
* Событие посылается, когда загрузка ресурса успешно завершена.
|
||||
*
|
||||
* @eventType flash.events.Event.COMPLETE
|
||||
*/
|
||||
[Event (name="complete", type="flash.events.Event")]
|
||||
/**
|
||||
* Событие посылается при возникновении ошибки загрузки.
|
||||
*
|
||||
* @eventType flash.events.IOErrorEvent.IO_ERROR
|
||||
*/
|
||||
[Event (name="ioError", type="flash.events.IOErrorEvent")]
|
||||
/**
|
||||
* Событие посылается, когда начинается загрузка очередной части ресурса.
|
||||
*
|
||||
* @eventType alternativa.engine3d.loaders.events.LoaderEvent.PART_OPEN
|
||||
*/
|
||||
[Event (name="partOpen", type="alternativa.engine3d.loaders.events.LoaderEvent")]
|
||||
/**
|
||||
* Событие посылается, когда загрузка очередной части ресурса успешно завершена.
|
||||
*
|
||||
* @eventType alternativa.engine3d.loaders.events.LoaderEvent.PART_COMPLETE
|
||||
*/
|
||||
[Event (name="partComplete", type="alternativa.engine3d.loaders.events.LoaderEvent")]
|
||||
/**
|
||||
* Событие посылается для отображения прогресса загрузки.
|
||||
*
|
||||
* @eventType alternativa.engine3d.loaders.events.LoaderProgressEvent.LOADER_PROGRESS
|
||||
*/
|
||||
[Event (name="loaderProgress", type="alternativa.engine3d.loaders.events.LoaderProgressEvent")]
|
||||
|
||||
/**
|
||||
* Загрузчик текстуры, состоящей из одного или двух файлов. В случае, если указан второй файл, он используется для заполнения альфа-канала
|
||||
* получаемой текстуры.
|
||||
*/
|
||||
public class TextureLoader extends EventDispatcher {
|
||||
|
||||
private static const IDLE:int = -1;
|
||||
private static const LOADING_DIFFUSE_MAP:int = 0;
|
||||
private static const LOADING_ALPHA_MAP:int = 1;
|
||||
|
||||
private var state:int = IDLE;
|
||||
private var bitmapLoader:Loader;
|
||||
private var loaderContext:LoaderContext;
|
||||
private var alphaTextureUrl:String;
|
||||
private var _bitmapData:BitmapData;
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр. Если указан URL диффузной части текстуры, то сразу начинается загрузка.
|
||||
*
|
||||
* @param diffuseTextureUrl URL диффузной части текстуры
|
||||
* @param alphaTextureUrl URL карты прозрачности
|
||||
* @param loaderContext LoaderContext, используемый при загрузке
|
||||
*/
|
||||
public function TextureLoader() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Загруженная текстура.
|
||||
*/
|
||||
public function get bitmapData():BitmapData {
|
||||
return _bitmapData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузка текстурных карт. При успешной загрузке посылается сообщение <code>Event.COMPLETE</code>.
|
||||
*
|
||||
* @param diffuseTextureUrl URL файла диффузной карты
|
||||
* @param alphaTextureUrl URL файла карты прозрачности
|
||||
* @param loaderContext LoaderContext, используемый при загрузке
|
||||
*/
|
||||
public function load(diffuseTextureUrl:String, alphaTextureUrl:String = null, loaderContext:LoaderContext = null):void {
|
||||
unload();
|
||||
this.alphaTextureUrl = alphaTextureUrl == "" ? null : alphaTextureUrl;
|
||||
this.loaderContext = loaderContext;
|
||||
|
||||
loadPart(LOADING_DIFFUSE_MAP, diffuseTextureUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Прекращвет текущую загрузку. Если нет активных загрузок, не происходит ничего.
|
||||
*/
|
||||
public function close():void {
|
||||
if (state == IDLE) return;
|
||||
state = IDLE;
|
||||
bitmapLoader.unload();
|
||||
destroyLoader();
|
||||
alphaTextureUrl = null;
|
||||
loaderContext = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает внутренние ссылки на загруженные объекты, чтобы сборщик мусора смог их удалить.
|
||||
*/
|
||||
public function unload():void {
|
||||
close();
|
||||
_bitmapData = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает временные внутренние ссылки.
|
||||
*/
|
||||
private function cleanup():void {
|
||||
destroyLoader();
|
||||
alphaTextureUrl = null;
|
||||
loaderContext = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Запускает загрузку части текстуры.
|
||||
*
|
||||
* @param state фаза загрузки
|
||||
* @param url URL загружаемого файла
|
||||
*/
|
||||
private function loadPart(state:int, url:String):void {
|
||||
this.state = state;
|
||||
createLoader();
|
||||
bitmapLoader.load(new URLRequest(url), loaderContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Обрабатывает начало загрузки очередной части текстуры.
|
||||
*/
|
||||
private function onPartLoadingOpen(e:Event):void {
|
||||
if (_bitmapData == null && hasEventListener(Event.OPEN)) {
|
||||
dispatchEvent(new Event(Event.OPEN));
|
||||
}
|
||||
if (hasEventListener(LoaderEvent.PART_OPEN)) {
|
||||
dispatchEvent(new LoaderEvent(LoaderEvent.PART_OPEN, 2, state == LOADING_DIFFUSE_MAP ? 0 : 1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function onPartLoadingProgress(e:ProgressEvent):void {
|
||||
if (hasEventListener(LoaderProgressEvent.LOADER_PROGRESS)) {
|
||||
var partNumber:int = state == LOADING_DIFFUSE_MAP ? 0 : 1;
|
||||
var totalProgress:Number = 0.5*(partNumber + e.bytesLoaded/e.bytesTotal);
|
||||
dispatchEvent(new LoaderProgressEvent(LoaderProgressEvent.LOADER_PROGRESS, 2, partNumber, totalProgress, e.bytesLoaded, e.bytesTotal));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function onPartLoadingComplete(e:Event):void {
|
||||
switch (state) {
|
||||
case LOADING_DIFFUSE_MAP: {
|
||||
// Загрузилась диффузная текстура. При необходимости загружается карта прозрачности.
|
||||
_bitmapData = Bitmap(bitmapLoader.content).bitmapData;
|
||||
destroyLoader();
|
||||
dispatchPartComplete(0);
|
||||
if (alphaTextureUrl != null) {
|
||||
loadPart(LOADING_ALPHA_MAP, alphaTextureUrl);
|
||||
} else {
|
||||
complete();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LOADING_ALPHA_MAP: {
|
||||
// Загрузилась карта прозрачности. Выполняется копирование прозрачности в альфа-канал диффузной текстуры.
|
||||
var pt:Point = new Point();
|
||||
var tmpBmd:BitmapData = _bitmapData;
|
||||
_bitmapData = new BitmapData(_bitmapData.width, _bitmapData.height);
|
||||
_bitmapData.copyPixels(tmpBmd, tmpBmd.rect, pt);
|
||||
|
||||
var alpha:BitmapData = Bitmap(bitmapLoader.content).bitmapData;
|
||||
destroyLoader();
|
||||
if (_bitmapData.width != alpha.width || _bitmapData.height != alpha.height) {
|
||||
tmpBmd.draw(alpha, new Matrix(_bitmapData.width/alpha.width, 0, 0, _bitmapData.height/alpha.height), null, BlendMode.NORMAL, null, true);
|
||||
alpha.dispose();
|
||||
alpha = tmpBmd;
|
||||
} else {
|
||||
tmpBmd.dispose();
|
||||
}
|
||||
_bitmapData.copyChannel(alpha, alpha.rect, pt, BitmapDataChannel.RED, BitmapDataChannel.ALPHA);
|
||||
alpha.dispose();
|
||||
dispatchPartComplete(1);
|
||||
complete();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт событие завершения загрузки части текстуры.
|
||||
*
|
||||
* @param partnNumber номер загруженной части текстуры
|
||||
*/
|
||||
private function dispatchPartComplete(partNumber:int):void {
|
||||
if (hasEventListener(LoaderEvent.PART_COMPLETE)) {
|
||||
dispatchEvent(new LoaderEvent(LoaderEvent.PART_COMPLETE, 2, partNumber));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function onLoadError(e:Event):void {
|
||||
state = IDLE;
|
||||
cleanup();
|
||||
dispatchEvent(e);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function complete():void {
|
||||
state = IDLE;
|
||||
cleanup();
|
||||
if (hasEventListener(Event.COMPLETE)) {
|
||||
dispatchEvent(new Event(Event.COMPLETE));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function createLoader():void {
|
||||
bitmapLoader = new Loader();
|
||||
var loaderInfo:LoaderInfo = bitmapLoader.contentLoaderInfo;
|
||||
loaderInfo.addEventListener(Event.OPEN, onPartLoadingOpen);
|
||||
loaderInfo.addEventListener(ProgressEvent.PROGRESS, onPartLoadingProgress);
|
||||
loaderInfo.addEventListener(Event.COMPLETE, onPartLoadingComplete);
|
||||
loaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onLoadError);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyLoader():void {
|
||||
if (bitmapLoader == null) return;
|
||||
bitmapLoader.unload();
|
||||
var loaderInfo:LoaderInfo = bitmapLoader.contentLoaderInfo;
|
||||
loaderInfo.removeEventListener(Event.OPEN, onPartLoadingOpen);
|
||||
loaderInfo.removeEventListener(ProgressEvent.PROGRESS, onPartLoadingProgress);
|
||||
loaderInfo.removeEventListener(Event.COMPLETE, onPartLoadingComplete);
|
||||
loaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
|
||||
bitmapLoader = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package alternativa.engine3d.loaders.events {
|
||||
import flash.events.ErrorEvent;
|
||||
import flash.events.Event;
|
||||
|
||||
/**
|
||||
* Класс представляет событие ошибки, генерируемое пакетным загрузчиком текстур.
|
||||
*/
|
||||
public class BatchTextureLoaderErrorEvent extends ErrorEvent {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static const LOADER_ERROR:String = "loaderError";
|
||||
|
||||
// Имя текстуры, с которой произошла проблема
|
||||
private var _textureName:String;
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр.
|
||||
*
|
||||
* @param type тип события
|
||||
* @param textureName имя текстуры, с которой произошла проблема
|
||||
* @param text описание ошибки
|
||||
*/
|
||||
public function BatchTextureLoaderErrorEvent(type:String, textureName:String, text:String) {
|
||||
super(type);
|
||||
this.text = text;
|
||||
_textureName = textureName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Имя текстуры, с которой произошла проблема.
|
||||
*/
|
||||
public function get textureName():String {
|
||||
return _textureName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Клонирует объект.
|
||||
*
|
||||
* @return клон объекта
|
||||
*/
|
||||
override public function clone():Event {
|
||||
return new BatchTextureLoaderErrorEvent(type, _textureName, text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт строкове представление объекта.
|
||||
*
|
||||
* @return строкове представление объекта
|
||||
*/
|
||||
override public function toString():String {
|
||||
return "[BatchTextureLoaderErrorEvent textureName=" + _textureName + ", text=" + text + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package alternativa.engine3d.loaders.events {
|
||||
import flash.events.Event;
|
||||
|
||||
/**
|
||||
* Событие загрузчиков ресурсов, состоящих из нескольких частей.
|
||||
*/
|
||||
public class LoaderEvent extends Event {
|
||||
/**
|
||||
* Событие начала загрузки очередной части ресурса.
|
||||
*/
|
||||
public static const PART_OPEN:String = "partOpen";
|
||||
/**
|
||||
* Событие окончания загрузки очередной части ресурса.
|
||||
*/
|
||||
public static const PART_COMPLETE:String = "partComplete";
|
||||
|
||||
// Общее количество загружаемых частей
|
||||
private var _partsTotal:int;
|
||||
// Номер загружаемой в настоящий момент части. Нумерация начинается с нуля.
|
||||
private var _currentPart:int;
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр.
|
||||
*
|
||||
* @param type тип события
|
||||
* @param totalParts общее количество загружаемых частей
|
||||
* @param currentPart номер части, к которой относится событие. Нумерация начинается с нуля
|
||||
*/
|
||||
public function LoaderEvent(type:String, partsTotal:int, currentPart:int) {
|
||||
super(type);
|
||||
_partsTotal = partsTotal;
|
||||
_currentPart= currentPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Общее количество загружаемых частей.
|
||||
*/
|
||||
public function get partsTotal():int {
|
||||
return _partsTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Номер загружаемой в настоящий момент части. Нумерация начинается с нуля.
|
||||
*/
|
||||
public function get currentPart():int {
|
||||
return _currentPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Клонирует объект.
|
||||
*
|
||||
* @return клон объекта
|
||||
*/
|
||||
override public function clone():Event {
|
||||
return new LoaderEvent(type, _partsTotal, _currentPart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт строкове представление объекта.
|
||||
*
|
||||
* @return строкове представление объекта
|
||||
*/
|
||||
override public function toString():String {
|
||||
return "[LoaderEvent type=" + type + ", partsTotal=" + _partsTotal + ", currentPart=" + _currentPart + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package alternativa.engine3d.loaders.events {
|
||||
import flash.events.Event;
|
||||
import flash.events.ProgressEvent;
|
||||
|
||||
/**
|
||||
* Событие прогресса загрузки ресурсов, состоящих из нескольких частей.
|
||||
*/
|
||||
public class LoaderProgressEvent extends ProgressEvent {
|
||||
|
||||
/**
|
||||
* Событие прогресса загрузки очередной части ресурса.
|
||||
*/
|
||||
public static const LOADER_PROGRESS:String = "loaderProgress";
|
||||
|
||||
// Общее количество загружаемых частей
|
||||
private var _partsTotal:int;
|
||||
// Номер загружаемой в настоящий момент части. Нумерация начинается с нуля.
|
||||
private var _currentPart:int;
|
||||
// Общий прогресс загрузки, выраженный числом в интервале [0, 1]
|
||||
private var _totalProgress:Number = 0;
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр.
|
||||
*
|
||||
* @param type тип события
|
||||
* @param totalParts общее количество загружаемых частей
|
||||
* @param currentPart номер загружаемой в настоящий момент части. Нумерация начинается с нуля
|
||||
* @param totalProgress общий прогресс загрузки, выраженный числом в интервале [0, 1]
|
||||
* @param bytesLoaded количество загруженных байт текущей части
|
||||
* @param bytesTotal объём текущей части
|
||||
*/
|
||||
public function LoaderProgressEvent(type:String, partsTotal:int, currentPart:int, totalProgress:Number = 0, bytesLoaded:uint=0, bytesTotal:uint=0) {
|
||||
super(type, false, false, bytesLoaded, bytesTotal);
|
||||
_partsTotal = partsTotal;
|
||||
_currentPart= currentPart;
|
||||
_totalProgress = totalProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Общее количество загружаемых частей.
|
||||
*/
|
||||
public function get partsTotal():int {
|
||||
return _partsTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Номер загружаемой в настоящий момент части. Нумерация начинается с нуля.
|
||||
*/
|
||||
public function get currentPart():int {
|
||||
return _currentPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Общий прогресс загрузки, выраженный числом в интервале [0, 1].
|
||||
*/
|
||||
public function get totalProgress():Number {
|
||||
return _totalProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Клонирует объект.
|
||||
*
|
||||
* @return клон объекта
|
||||
*/
|
||||
override public function clone():Event {
|
||||
return new LoaderProgressEvent(type, _partsTotal, _currentPart, _totalProgress, bytesLoaded, bytesTotal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт строкове представление объекта.
|
||||
*
|
||||
* @return строкове представление объекта
|
||||
*/
|
||||
override public function toString():String {
|
||||
return "[LoaderProgressEvent partsTotal=" + _partsTotal + ", currentPart=" + _currentPart + ", totalProgress=" + _totalProgress.toFixed(2) + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package alternativa.engine3d.objects {
|
||||
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.MipMap;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.display.BitmapData;
|
||||
import flash.geom.Matrix3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Анимированный спрайт.
|
||||
* Анимация осуществляется путём переключения изображений,
|
||||
* хранящихся в списке textures
|
||||
*/
|
||||
public class AnimSprite extends Sprite3D {
|
||||
|
||||
/**
|
||||
* Список кадров изображений
|
||||
*/
|
||||
public var textures:Vector.<BitmapData>;
|
||||
public var mipMaps:Vector.<MipMap>;
|
||||
/**
|
||||
* Устанавливаемый кадр
|
||||
*/
|
||||
public var frame:uint = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
if (mipMapping == 0) texture = textures[frame]; else mipMap = mipMaps[frame];
|
||||
super.draw(camera, object, parentCanvas);
|
||||
}
|
||||
|
||||
override public function calculateBoundBox(matrix:Matrix3D = null, boundBox:BoundBox = null):BoundBox {
|
||||
if (mipMapping == 0) texture = textures[frame]; else mipMap = mipMaps[frame];
|
||||
return super.calculateBoundBox(matrix, boundBox);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
120
Alternativa3D7/7.1/alternativa/engine3d/objects/Axes.as
Normal file
120
Alternativa3D7/7.1/alternativa/engine3d/objects/Axes.as
Normal file
@@ -0,0 +1,120 @@
|
||||
package alternativa.engine3d.objects {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.display.Graphics;
|
||||
import flash.geom.Utils3D;
|
||||
import flash.text.TextField;
|
||||
import flash.text.TextFieldAutoSize;
|
||||
import flash.text.TextFormat;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Вспомогательный объект, иллюстрирующий систему коордщинат
|
||||
*/
|
||||
public class Axes extends Object3D {
|
||||
|
||||
public var axisLength:Number;
|
||||
public var lineThickness:Number;
|
||||
public var dotRadius:Number;
|
||||
public var textSize:Number;
|
||||
|
||||
public function Axes(axisLength:Number = 30, lineThickness:Number = 0, dotRadius:Number = 2, textSize:Number = 10):void {
|
||||
this.axisLength = axisLength;
|
||||
this.lineThickness = lineThickness;
|
||||
this.dotRadius = dotRadius;
|
||||
this.textSize = textSize;
|
||||
_boundBox = new BoundBox();
|
||||
_boundBox.setSize(-dotRadius, -dotRadius, -dotRadius, axisLength, axisLength, axisLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
|
||||
var p:Vector.<Number> = Vector.<Number>([0, 0, 0, axisLength, 0, 0, 0, axisLength, 0, 0, 0, axisLength]);
|
||||
var d:Vector.<Number> = new Vector.<Number>(8);
|
||||
object.cameraMatrix.transformVectors(p, p);
|
||||
|
||||
// Центр за камерой
|
||||
if (p[2] < camera.nearClipping) return;
|
||||
|
||||
Utils3D.projectVectors(camera.projectionMatrix, p, d, new Vector.<Number>());
|
||||
var size:Number = camera.viewSize/p[2];
|
||||
|
||||
// Подготовка канваса
|
||||
var canvas:Canvas = parentCanvas.getChildCanvas(true, false, object.alpha, object.blendMode, object.colorTransform, object.filters);
|
||||
|
||||
var gfx:Graphics = canvas.gfx;
|
||||
var text:TextField;
|
||||
|
||||
// Ось X
|
||||
if (p[5] >= camera.nearClipping) {
|
||||
gfx.lineStyle(lineThickness*size, 0xFF0000);
|
||||
gfx.moveTo(d[0], d[1]);
|
||||
gfx.lineTo(d[2], d[3]);
|
||||
|
||||
text = new TextField();
|
||||
text.autoSize = TextFieldAutoSize.LEFT;
|
||||
text.selectable = false;
|
||||
text.x = d[2];
|
||||
text.y = d[3];
|
||||
text.text = "X";
|
||||
text.setTextFormat(new TextFormat("Tahoma", textSize*size, 0xFF0000));
|
||||
|
||||
canvas.addChild(text);
|
||||
canvas._numChildren++;
|
||||
}
|
||||
|
||||
// Ось Y
|
||||
if (p[8] >= camera.nearClipping) {
|
||||
gfx.lineStyle(lineThickness*size, 0x00FF00);
|
||||
gfx.moveTo(d[0], d[1]);
|
||||
gfx.lineTo(d[4], d[5]);
|
||||
|
||||
text = new TextField();
|
||||
text.autoSize = TextFieldAutoSize.LEFT;
|
||||
text.selectable = false;
|
||||
text.x = d[4];
|
||||
text.y = d[5];
|
||||
text.text = "Y";
|
||||
text.setTextFormat(new TextFormat("Tahoma", textSize*size, 0x00FF00));
|
||||
|
||||
canvas.addChild(text);
|
||||
canvas._numChildren++;
|
||||
}
|
||||
|
||||
// Ось Z
|
||||
if (p[11] >= camera.nearClipping) {
|
||||
gfx.lineStyle(lineThickness*size, 0x0000FF);
|
||||
gfx.moveTo(d[0], d[1]);
|
||||
gfx.lineTo(d[6], d[7]);
|
||||
|
||||
text = new TextField();
|
||||
text.autoSize = TextFieldAutoSize.LEFT;
|
||||
text.selectable = false;
|
||||
text.x = d[6];
|
||||
text.y = d[7];
|
||||
text.text = "Z";
|
||||
text.setTextFormat(new TextFormat("Tahoma", textSize*size, 0x0000FF));
|
||||
|
||||
canvas.addChild(text);
|
||||
canvas._numChildren++;
|
||||
}
|
||||
|
||||
// Начало координат
|
||||
gfx.lineStyle();
|
||||
gfx.beginFill(0xFFFFFF);
|
||||
gfx.drawCircle(d[0], d[1], dotRadius*size);
|
||||
|
||||
//debugDrawBoundRaduis(camera, object, canvas);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
58
Alternativa3D7/7.1/alternativa/engine3d/objects/Bone.as
Normal file
58
Alternativa3D7/7.1/alternativa/engine3d/objects/Bone.as
Normal file
@@ -0,0 +1,58 @@
|
||||
package alternativa.engine3d.objects {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import flash.geom.Matrix3D;
|
||||
import __AS3__.vec.Vector;
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class Bone extends Mesh {
|
||||
|
||||
public var localTransform:Vector.<Vector3D>;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var localMatrix:Matrix3D;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var length:Number;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var distance:Number;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _numChildren:uint = 0;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var children:Vector.<Bone> = new Vector.<Bone>();
|
||||
|
||||
public function Bone(length:Number, distance:Number) {
|
||||
this.length = length;
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
public function addChild(child:Bone):void {
|
||||
children[_numChildren++] = child;
|
||||
child.localTransform = child.matrix.decompose();
|
||||
child.localMatrix = new Matrix3D();
|
||||
}
|
||||
|
||||
public function calculateMatrix():void {
|
||||
for (var i:int = 0; i < _numChildren; i++) {
|
||||
var child:Bone = children[i];
|
||||
child.matrix.identity();
|
||||
child.matrix.prepend(matrix);
|
||||
child.localMatrix.recompose(child.localTransform);
|
||||
child.matrix.prepend(child.localMatrix);
|
||||
child.calculateMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
1305
Alternativa3D7/7.1/alternativa/engine3d/objects/KDObject.as
Normal file
1305
Alternativa3D7/7.1/alternativa/engine3d/objects/KDObject.as
Normal file
File diff suppressed because it is too large
Load Diff
56
Alternativa3D7/7.1/alternativa/engine3d/objects/LOD.as
Normal file
56
Alternativa3D7/7.1/alternativa/engine3d/objects/LOD.as
Normal file
@@ -0,0 +1,56 @@
|
||||
package alternativa.engine3d.objects {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.geom.Matrix3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Объект, имеющий набор объектов с разной детализацией.
|
||||
* При отрисовке, он выбирает в зависимости от расстояния от камеры
|
||||
* объект с нужной детализацией и отрисовывает его вместо себя.
|
||||
* Это позволяет получить лучший визуальный результат и большую производительность.
|
||||
*/
|
||||
public class LOD extends Object3D {
|
||||
|
||||
/**
|
||||
* Объекты с разной детализацией
|
||||
*/
|
||||
public var lodObjects:Vector.<Object3D>;
|
||||
/**
|
||||
* Расстояния до камеры соответствующие объектам с разной детализацией
|
||||
*/
|
||||
public var lodDistances:Vector.<Number>;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
var cameraDistance:Number = object.cameraMatrix.position.length;
|
||||
|
||||
// Поиск ближайшего лода
|
||||
var min:Number = Infinity;
|
||||
var length:uint = lodObjects.length;
|
||||
var lod:Object3D;
|
||||
for (var i:uint = 0; i < length; i++) {
|
||||
var d:Number = Math.abs(cameraDistance - lodDistances[i]);
|
||||
if (d < min) {
|
||||
min = d;
|
||||
lod = lodObjects[i];
|
||||
}
|
||||
}
|
||||
if (camera.debugMode) lod.debug(camera, object, parentCanvas);
|
||||
lod.draw(camera, object, parentCanvas);
|
||||
}
|
||||
|
||||
override public function calculateBoundBox(matrix:Matrix3D = null, boundBox:BoundBox = null):BoundBox {
|
||||
return (lodObjects[0] as Object3D).calculateBoundBox(matrix, boundBox);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
2042
Alternativa3D7/7.1/alternativa/engine3d/objects/Mesh.as
Normal file
2042
Alternativa3D7/7.1/alternativa/engine3d/objects/Mesh.as
Normal file
File diff suppressed because it is too large
Load Diff
404
Alternativa3D7/7.1/alternativa/engine3d/objects/Occluder.as
Normal file
404
Alternativa3D7/7.1/alternativa/engine3d/objects/Occluder.as
Normal file
@@ -0,0 +1,404 @@
|
||||
package alternativa.engine3d.objects {
|
||||
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Utils3D;
|
||||
import alternativa.engine3d.core.Debug;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Полигональный объект-перекрытие.
|
||||
* Объекты, которые он перекрывает от видимости камеры, исключаются из отрисовки.
|
||||
* Сам окклюдер не отрисовывается.
|
||||
* Должен быть конвексным
|
||||
*/
|
||||
public class Occluder extends Object3D {
|
||||
/**
|
||||
* Режим представления полигонов.
|
||||
* Если false, в indices записаны треугольники (тройки индексов).
|
||||
* Если true, в indices записаны многоугольники в виде: количество вершин грани, индексы вершин
|
||||
*/
|
||||
public var poly:Boolean = false;
|
||||
public var vertices:Vector.<Number>;
|
||||
public var edges:Vector.<int>; // Два индекса - вершины, два - грани
|
||||
public var indices:Vector.<int>;
|
||||
public var normals:Vector.<Number>;
|
||||
|
||||
// Отношение площади перекрытия к площади вьюпорта (0 - 1)
|
||||
/**
|
||||
* Минимальное отношение площади перекрытия окклюдером вьюпорта к площади вьюпорта (от 0 до 1)
|
||||
* Если окклюдер перекрывает больше, он помещается в очередь и учитывается
|
||||
* при дальнейшей отрисовке в пределах кадра, иначе игнорируется
|
||||
*/
|
||||
public var minSize:Number = 0;
|
||||
|
||||
private const cameraVertices:Vector.<Number> = new Vector.<Number>();
|
||||
private const visibilityMap:Vector.<Boolean> = new Vector.<Boolean>;
|
||||
|
||||
/**
|
||||
* Коприрование геометрии меша
|
||||
* @param mesh Объект копирования
|
||||
* Меш, геометрия которого копируется, обязан быть конвексным, иначе окклюдер будет некорректно работать
|
||||
*/
|
||||
public function copyFrom(mesh:Mesh):void {
|
||||
poly = mesh.poly;
|
||||
vertices = mesh.vertices;
|
||||
indices = mesh.indices;
|
||||
normals = mesh.normals;
|
||||
|
||||
matrix.identity();
|
||||
matrix.prepend(mesh.matrix);
|
||||
if (_boundBox != null) {
|
||||
_boundBox.copyFrom(mesh._boundBox);
|
||||
} else {
|
||||
_boundBox = mesh._boundBox;
|
||||
}
|
||||
}
|
||||
|
||||
override public function calculateBoundBox(matrix:Matrix3D = null, boundBox:BoundBox = null):BoundBox {
|
||||
var v:Vector.<Number> = vertices;
|
||||
// Если указана матрица трансформации, переводим
|
||||
if (matrix != null) {
|
||||
matrix.transformVectors(vertices, cameraVertices);
|
||||
v = cameraVertices;
|
||||
}
|
||||
// Если указан баунд-бокс
|
||||
if (boundBox != null) {
|
||||
boundBox.infinity();
|
||||
} else {
|
||||
boundBox = new BoundBox();
|
||||
}
|
||||
// Ищем баунд-бокс
|
||||
for (var i:int = 0, length:int = vertices.length; i < length;) {
|
||||
boundBox.addPoint(v[i++], v[i++], v[i++]);
|
||||
}
|
||||
return boundBox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Расчёт рёбер по имеющимся вершинам и граням
|
||||
*/
|
||||
public function calculateEdges():void {
|
||||
// Подготавливаем массив рёбер
|
||||
if (edges == null) edges = new Vector.<int>();
|
||||
|
||||
// Собираем рёбра
|
||||
for (var i:int = 0, j:int = 0, n:int = 0, k:int = 0, a:int, b:int, length:int = indices.length; i < length;) {
|
||||
if (i == k) {
|
||||
k = poly ? (indices[i++] + i) : (i + 3);
|
||||
a = indices[int(k - 1)];
|
||||
}
|
||||
b = indices[i];
|
||||
edges[j++] = a;
|
||||
edges[j++] = b;
|
||||
edges[j++] = n;
|
||||
edges[j++] = -1;
|
||||
if (++i == k) n++; else a = b;
|
||||
}
|
||||
edges.length = j;
|
||||
|
||||
// Убираем дубли
|
||||
length = j, i = 0; k = 0;
|
||||
var ac:int, bc:int;
|
||||
while (i < length) {
|
||||
if ((a = edges[i++]) >= 0) {
|
||||
b = edges[i++];
|
||||
edges[k++] = a;
|
||||
edges[k++] = b;
|
||||
edges[k++] = edges[i++];
|
||||
j = ++i;
|
||||
while (j < length) {
|
||||
ac = edges[j++];
|
||||
bc = edges[j++];
|
||||
if (ac == a && bc == b || ac == b && bc == a) {
|
||||
edges[int(j - 2)] = -1;
|
||||
edges[k] = edges[j];
|
||||
break;
|
||||
}
|
||||
j += 2;
|
||||
}
|
||||
k++;
|
||||
} else i += 3;
|
||||
}
|
||||
edges.length = k;
|
||||
}
|
||||
|
||||
static private const inverseCameraMatrix:Matrix3D = new Matrix3D();
|
||||
static private const center:Vector.<Number> = new Vector.<Number>(3, true);
|
||||
private var cameraX:Number;
|
||||
private var cameraY:Number;
|
||||
private var cameraZ:Number;
|
||||
static private const projectedEdges:Vector.<Number> = new Vector.<Number>();
|
||||
static private const uvts:Vector.<Number> = new Vector.<Number>();
|
||||
static private const viewEdges:Vector.<Number> = new Vector.<Number>();
|
||||
static private const debugEdges:Vector.<Number> = new Vector.<Number>();
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
// Перевод в координаты камеры
|
||||
object.cameraMatrix.transformVectors(vertices, cameraVertices);
|
||||
// Определение центра камеры в объекте
|
||||
inverseCameraMatrix.identity();
|
||||
inverseCameraMatrix.prepend(object.cameraMatrix);
|
||||
inverseCameraMatrix.invert();
|
||||
center[0] = center[1] = center[2] = 0;
|
||||
inverseCameraMatrix.transformVectors(center, center);
|
||||
cameraX = center[0], cameraY = center[1], cameraZ = center[2];
|
||||
// Расчёт карты видимости граней
|
||||
for (var i:int = 0, n:int = 0, normalsLength:int = normals.length >> 2, cameraInside:Boolean = true, infront:Boolean; i < normalsLength;) visibilityMap[i++] = infront = normals[n++]*cameraX + normals[n++]*cameraY + normals[n++]*cameraZ > normals[n++], cameraInside &&= !infront;
|
||||
// Если камера внутри окклюдера
|
||||
if (cameraInside) return;
|
||||
// Подготовка окклюдера в камере
|
||||
var occludeAll:Boolean = true, culling:int = object.culling, direction:Boolean, ax:Number, ay:Number, az:Number, bx:Number, by:Number, bz:Number, t:Number;
|
||||
var planeOccluder:Vector.<Number>, edgeOccluder:Vector.<Number>, planeOccluderLength:int = 0, edgeOccluderLength:int = 0, viewEdgesLength:int = 0;
|
||||
if (camera.occlusionPlanes.length > camera.numOccluders) planeOccluder = camera.occlusionPlanes[camera.numOccluders], edgeOccluder = camera.occlusionEdges[camera.numOccluders] else planeOccluder = camera.occlusionPlanes[camera.numOccluders] = new Vector.<Number>(), edgeOccluder = camera.occlusionEdges[camera.numOccluders] = new Vector.<Number>();
|
||||
for (i = edges.length - 1; i > 0;) {
|
||||
if ((direction = visibilityMap[edges[i--]]) != visibilityMap[edges[i--]]) {
|
||||
// Определение порядка вершин (против часовой)
|
||||
if (direction) ax = cameraVertices[n = int(edges[i--]*3)], ay = cameraVertices[++n], az = cameraVertices[++n], bx = cameraVertices[n = int(edges[i--]*3)], by = cameraVertices[++n], bz = cameraVertices[++n] else bx = cameraVertices[n = int(edges[i--]*3)], by = cameraVertices[++n], bz = cameraVertices[++n], ax = cameraVertices[n = int(edges[i--]*3)], ay = cameraVertices[++n], az = cameraVertices[++n];
|
||||
// Клиппинг
|
||||
if (culling > 0) {
|
||||
if (az <= -ax && bz <= -bx) {
|
||||
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
|
||||
continue;
|
||||
} else if (bz > -bx && az <= -ax) {
|
||||
t = (ax + az)/(ax + az - bx - bz), ax = ax + (bx - ax)*t, ay = ay + (by - ay)*t, az = az + (bz - az)*t;
|
||||
} else if (bz <= -bx && az > -ax) {
|
||||
t = (ax + az)/(ax + az - bx - bz), bx = ax + (bx - ax)*t, by = ay + (by - ay)*t, bz = az + (bz - az)*t;
|
||||
}
|
||||
if (az <= ax && bz <= bx) {
|
||||
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
|
||||
continue;
|
||||
} else if (bz > bx && az <= ax) {
|
||||
t = (az - ax)/(az - ax + bx - bz), ax = ax + (bx - ax)*t, ay = ay + (by - ay)*t, az = az + (bz - az)*t;
|
||||
} else if (bz <= bx && az > ax) {
|
||||
t = (az - ax)/(az - ax + bx - bz), bx = ax + (bx - ax)*t, by = ay + (by - ay)*t, bz = az + (bz - az)*t;
|
||||
}
|
||||
if (az <= -ay && bz <= -by) {
|
||||
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
|
||||
continue;
|
||||
} else if (bz > -by && az <= -ay) {
|
||||
t = (ay + az)/(ay + az - by - bz), ax = ax + (bx - ax)*t, ay = ay + (by - ay)*t, az = az + (bz - az)*t;
|
||||
} else if (bz <= -by && az > -ay) {
|
||||
t = (ay + az)/(ay + az - by - bz), bx = ax + (bx - ax)*t, by = ay + (by - ay)*t, bz = az + (bz - az)*t;
|
||||
}
|
||||
if (az <= ay && bz <= by) {
|
||||
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
|
||||
continue;
|
||||
} else if (bz > by && az <= ay) {
|
||||
t = (az - ay)/(az - ay + by - bz), ax = ax + (bx - ax)*t, ay = ay + (by - ay)*t, az = az + (bz - az)*t;
|
||||
} else if (bz <= by && az > ay) {
|
||||
t = (az - ay)/(az - ay + by - bz), bx = ax + (bx - ax)*t, by = ay + (by - ay)*t, bz = az + (bz - az)*t;
|
||||
}
|
||||
occludeAll = false;
|
||||
}
|
||||
// Расчёт нормали плоскости отсечения
|
||||
planeOccluder[planeOccluderLength++] = bz*ay - by*az, planeOccluder[planeOccluderLength++] = bx*az - bz*ax, planeOccluder[planeOccluderLength++] = by*ax - bx*ay;
|
||||
// Сохранение рёбер
|
||||
edgeOccluder[edgeOccluderLength++] = ax, edgeOccluder[edgeOccluderLength++] = ay, edgeOccluder[edgeOccluderLength++] = az, edgeOccluder[edgeOccluderLength++] = bx, edgeOccluder[edgeOccluderLength++] = by, edgeOccluder[edgeOccluderLength++] = bz;
|
||||
} else i -= 2;
|
||||
}
|
||||
if (planeOccluderLength > 0) {
|
||||
// Проверка размера на экране
|
||||
if (minSize > 0) {
|
||||
// Проецирование рёбер контура
|
||||
var projectedEdgesLength:int = projectedEdges.length = ((edgeOccluder.length = edgeOccluderLength)/3) << 1;
|
||||
Utils3D.projectVectors(camera.projectionMatrix, edgeOccluder, projectedEdges, uvts);
|
||||
// Клиппинг рамки вьюпорта
|
||||
if (culling > 0) {
|
||||
if (culling & 4) viewEdges[viewEdgesLength++] = -camera.viewSizeX, viewEdges[viewEdgesLength++] = -camera.viewSizeY, viewEdges[viewEdgesLength++] = -camera.viewSizeX, viewEdges[viewEdgesLength++] = camera.viewSizeY;
|
||||
if (culling & 8) viewEdges[viewEdgesLength++] = camera.viewSizeX, viewEdges[viewEdgesLength++] = camera.viewSizeY, viewEdges[viewEdgesLength++] = camera.viewSizeX, viewEdges[viewEdgesLength++] = -camera.viewSizeY;
|
||||
if (culling & 16) viewEdges[viewEdgesLength++] = camera.viewSizeX, viewEdges[viewEdgesLength++] = -camera.viewSizeY, viewEdges[viewEdgesLength++] = -camera.viewSizeX, viewEdges[viewEdgesLength++] = -camera.viewSizeY;
|
||||
if (culling & 32) viewEdges[viewEdgesLength++] = -camera.viewSizeX, viewEdges[viewEdgesLength++] = camera.viewSizeY, viewEdges[viewEdgesLength++] = camera.viewSizeX, viewEdges[viewEdgesLength++] = camera.viewSizeY;
|
||||
if (viewEdgesLength > 0) {
|
||||
for (i = 0; i < projectedEdgesLength;) {
|
||||
ax = projectedEdges[i++], ay = projectedEdges[i++], bx = projectedEdges[i++], by = projectedEdges[i++];
|
||||
var nx:Number = ay - by, ny:Number = bx - ax, no:Number = nx*ax + ny*ay;
|
||||
for (var j:int = 0, j2:int = 0; j < viewEdgesLength;) {
|
||||
ax = viewEdges[j++], ay = viewEdges[j++], bx = viewEdges[j++], by = viewEdges[j++], az = ax*nx + ay*ny - no, bz = bx*nx + by*ny - no;
|
||||
if (az < 0 || bz < 0) {
|
||||
if (az >= 0 && bz < 0) {
|
||||
t = az/(az - bz), viewEdges[j2++] = ax + (bx - ax)*t, viewEdges[j2++] = ay + (by - ay)*t, viewEdges[j2++] = bx, viewEdges[j2++] = by;
|
||||
} else if (az < 0 && bz >= 0) {
|
||||
t = az/(az - bz), viewEdges[j2++] = ax, viewEdges[j2++] = ay, viewEdges[j2++] = ax + (bx - ax)*t, viewEdges[j2++] = ay + (by - ay)*t;
|
||||
} else {
|
||||
viewEdges[j2++] = ax, viewEdges[j2++] = ay, viewEdges[j2++] = bx, viewEdges[j2++] = by;
|
||||
}
|
||||
}
|
||||
}
|
||||
viewEdgesLength = j2;
|
||||
if (viewEdgesLength == 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Нахождение площади перекрытия
|
||||
var square:Number = 0;
|
||||
for (i = 0, az = projectedEdges[i++], bz = projectedEdges[i++], i += 2; i < projectedEdgesLength;) ax = projectedEdges[i++] - az, ay = projectedEdges[i++] - bz, bx = projectedEdges[i++] - az, by = projectedEdges[i++] - bz, square += bx*ay - by*ax;
|
||||
for (i = 0; i < viewEdgesLength;) ax = viewEdges[i++] - az, ay = viewEdges[i++] - bz, bx = viewEdges[i++] - az, by = viewEdges[i++] - bz, square += bx*ay - by*ax;
|
||||
if (square/(camera.viewSizeX*camera.viewSizeY*8) < minSize) return;
|
||||
}
|
||||
// Добавление окклюдера
|
||||
camera.numOccluders++, planeOccluder.length = planeOccluderLength;
|
||||
} else {
|
||||
if (occludeAll) camera.numOccluders = 0, camera.occludedAll = true else return;
|
||||
}
|
||||
}
|
||||
|
||||
override alternativa3d function debug(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
var inDebug:int = camera.checkInDebug(this);
|
||||
if (inDebug == 0) return;
|
||||
var canvas:Canvas = parentCanvas.getChildCanvas(true, false);
|
||||
|
||||
// Рёбра
|
||||
if (inDebug & Debug.EDGES) {
|
||||
object.cameraMatrix.transformVectors(vertices, cameraVertices);
|
||||
inverseCameraMatrix.identity();
|
||||
inverseCameraMatrix.prepend(object.cameraMatrix);
|
||||
inverseCameraMatrix.invert();
|
||||
center[0] = center[1] = center[2] = 0;
|
||||
inverseCameraMatrix.transformVectors(center, center);
|
||||
cameraX = center[0], cameraY = center[1], cameraZ = center[2];
|
||||
// Расчёт карты видимости граней
|
||||
for (var i:int = 0, n:int = 0, normalsLength:int = normals.length >> 2, cameraInside:Boolean = true, infront:Boolean; i < normalsLength;) visibilityMap[i++] = infront = normals[n++]*cameraX + normals[n++]*cameraY + normals[n++]*cameraZ > normals[n++], cameraInside &&= !infront;
|
||||
// Если камера внутри окклюдера
|
||||
if (!cameraInside) {
|
||||
// Подготовка окклюдера в камере
|
||||
var occludeAll:Boolean = true, culling:int = object.culling, direction:Boolean, ax:Number, ay:Number, az:Number, bx:Number, by:Number, bz:Number, t:Number;
|
||||
var debugEdgesLength:int = 0, viewEdgesLength:int = 0;
|
||||
for (i = edges.length - 1; i > 0;) {
|
||||
if ((direction = visibilityMap[edges[i--]]) != visibilityMap[edges[i--]]) {
|
||||
// Определение порядка вершин (против часовой)
|
||||
if (direction) ax = cameraVertices[n = int(edges[i--]*3)], ay = cameraVertices[++n], az = cameraVertices[++n], bx = cameraVertices[n = int(edges[i--]*3)], by = cameraVertices[++n], bz = cameraVertices[++n] else bx = cameraVertices[n = int(edges[i--]*3)], by = cameraVertices[++n], bz = cameraVertices[++n], ax = cameraVertices[n = int(edges[i--]*3)], ay = cameraVertices[++n], az = cameraVertices[++n];
|
||||
// Клиппинг
|
||||
if (culling > 0) {
|
||||
if (az <= -ax && bz <= -bx) {
|
||||
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
|
||||
continue;
|
||||
} else if (bz > -bx && az <= -ax) {
|
||||
t = (ax + az)/(ax + az - bx - bz), ax = ax + (bx - ax)*t, ay = ay + (by - ay)*t, az = az + (bz - az)*t;
|
||||
} else if (bz <= -bx && az > -ax) {
|
||||
t = (ax + az)/(ax + az - bx - bz), bx = ax + (bx - ax)*t, by = ay + (by - ay)*t, bz = az + (bz - az)*t;
|
||||
}
|
||||
if (az <= ax && bz <= bx) {
|
||||
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
|
||||
continue;
|
||||
} else if (bz > bx && az <= ax) {
|
||||
t = (az - ax)/(az - ax + bx - bz), ax = ax + (bx - ax)*t, ay = ay + (by - ay)*t, az = az + (bz - az)*t;
|
||||
} else if (bz <= bx && az > ax) {
|
||||
t = (az - ax)/(az - ax + bx - bz), bx = ax + (bx - ax)*t, by = ay + (by - ay)*t, bz = az + (bz - az)*t;
|
||||
}
|
||||
if (az <= -ay && bz <= -by) {
|
||||
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
|
||||
continue;
|
||||
} else if (bz > -by && az <= -ay) {
|
||||
t = (ay + az)/(ay + az - by - bz), ax = ax + (bx - ax)*t, ay = ay + (by - ay)*t, az = az + (bz - az)*t;
|
||||
} else if (bz <= -by && az > -ay) {
|
||||
t = (ay + az)/(ay + az - by - bz), bx = ax + (bx - ax)*t, by = ay + (by - ay)*t, bz = az + (bz - az)*t;
|
||||
}
|
||||
if (az <= ay && bz <= by) {
|
||||
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
|
||||
continue;
|
||||
} else if (bz > by && az <= ay) {
|
||||
t = (az - ay)/(az - ay + by - bz), ax = ax + (bx - ax)*t, ay = ay + (by - ay)*t, az = az + (bz - az)*t;
|
||||
} else if (bz <= by && az > ay) {
|
||||
t = (az - ay)/(az - ay + by - bz), bx = ax + (bx - ax)*t, by = ay + (by - ay)*t, bz = az + (bz - az)*t;
|
||||
}
|
||||
occludeAll = false;
|
||||
}
|
||||
debugEdges[debugEdgesLength++] = ax;
|
||||
debugEdges[debugEdgesLength++] = ay;
|
||||
debugEdges[debugEdgesLength++] = az;
|
||||
debugEdges[debugEdgesLength++] = bx;
|
||||
debugEdges[debugEdgesLength++] = by;
|
||||
debugEdges[debugEdgesLength++] = bz;
|
||||
} else i -= 2;
|
||||
}
|
||||
if (debugEdgesLength > 0) {
|
||||
// Проецирование рёбер контура
|
||||
var projectedEdgesLength:int = projectedEdges.length = ((debugEdges.length = debugEdgesLength)/3) << 1;
|
||||
Utils3D.projectVectors(camera.projectionMatrix, debugEdges, projectedEdges, uvts);
|
||||
// Проверка размера на экране
|
||||
var square:Number = Number.MAX_VALUE;
|
||||
if (minSize > 0) {
|
||||
// Клиппинг рамки вьюпорта
|
||||
if (culling > 0) {
|
||||
if (culling & 4) viewEdges[viewEdgesLength++] = -camera.viewSizeX, viewEdges[viewEdgesLength++] = -camera.viewSizeY, viewEdges[viewEdgesLength++] = -camera.viewSizeX, viewEdges[viewEdgesLength++] = camera.viewSizeY;
|
||||
if (culling & 8) viewEdges[viewEdgesLength++] = camera.viewSizeX, viewEdges[viewEdgesLength++] = camera.viewSizeY, viewEdges[viewEdgesLength++] = camera.viewSizeX, viewEdges[viewEdgesLength++] = -camera.viewSizeY;
|
||||
if (culling & 16) viewEdges[viewEdgesLength++] = camera.viewSizeX, viewEdges[viewEdgesLength++] = -camera.viewSizeY, viewEdges[viewEdgesLength++] = -camera.viewSizeX, viewEdges[viewEdgesLength++] = -camera.viewSizeY;
|
||||
if (culling & 32) viewEdges[viewEdgesLength++] = -camera.viewSizeX, viewEdges[viewEdgesLength++] = camera.viewSizeY, viewEdges[viewEdgesLength++] = camera.viewSizeX, viewEdges[viewEdgesLength++] = camera.viewSizeY;
|
||||
if (viewEdgesLength > 0) {
|
||||
for (i = 0; i < projectedEdgesLength;) {
|
||||
ax = projectedEdges[i++], ay = projectedEdges[i++], bx = projectedEdges[i++], by = projectedEdges[i++];
|
||||
var nx:Number = ay - by, ny:Number = bx - ax, no:Number = nx*ax + ny*ay;
|
||||
for (var j:int = 0, j2:int = 0; j < viewEdgesLength;) {
|
||||
ax = viewEdges[j++], ay = viewEdges[j++], bx = viewEdges[j++], by = viewEdges[j++], az = ax*nx + ay*ny - no, bz = bx*nx + by*ny - no;
|
||||
if (az < 0 || bz < 0) {
|
||||
if (az >= 0 && bz < 0) {
|
||||
t = az/(az - bz), viewEdges[j2++] = ax + (bx - ax)*t, viewEdges[j2++] = ay + (by - ay)*t, viewEdges[j2++] = bx, viewEdges[j2++] = by;
|
||||
} else if (az < 0 && bz >= 0) {
|
||||
t = az/(az - bz), viewEdges[j2++] = ax, viewEdges[j2++] = ay, viewEdges[j2++] = ax + (bx - ax)*t, viewEdges[j2++] = ay + (by - ay)*t;
|
||||
} else {
|
||||
viewEdges[j2++] = ax, viewEdges[j2++] = ay, viewEdges[j2++] = bx, viewEdges[j2++] = by;
|
||||
}
|
||||
}
|
||||
}
|
||||
viewEdgesLength = j2;
|
||||
if (viewEdgesLength == 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Нахождение площади перекрытия
|
||||
square = 0;
|
||||
for (i = 0, az = projectedEdges[i++], bz = projectedEdges[i++], i += 2; i < projectedEdgesLength;) ax = projectedEdges[i++] - az, ay = projectedEdges[i++] - bz, bx = projectedEdges[i++] - az, by = projectedEdges[i++] - bz, square += bx*ay - by*ax;
|
||||
for (i = 0; i < viewEdgesLength;) ax = viewEdges[i++] - az, ay = viewEdges[i++] - bz, bx = viewEdges[i++] - az, by = viewEdges[i++] - bz, square += bx*ay - by*ax;
|
||||
}
|
||||
if (canvas == null) canvas = parentCanvas.getChildCanvas(true, false);
|
||||
var color:int, thickness:Number;
|
||||
if (square/(camera.viewSizeX*camera.viewSizeY*8) >= minSize) {
|
||||
color = 0x0000FF, thickness = 3;
|
||||
} else {
|
||||
color = 0x0077AA, thickness = 1;
|
||||
}
|
||||
for (i = 0; i < projectedEdges.length;) {
|
||||
ax = projectedEdges[i++], ay = projectedEdges[i++], bx = projectedEdges[i++], by = projectedEdges[i++];
|
||||
canvas.gfx.moveTo(ax, ay);
|
||||
canvas.gfx.lineStyle(thickness, color);
|
||||
canvas.gfx.lineTo(ax + (bx - ax)*0.8, ay + (by - ay)*0.8);
|
||||
canvas.gfx.lineStyle(thickness, 0xFF0000);
|
||||
canvas.gfx.lineTo(bx, by);
|
||||
}
|
||||
for (i = 0; i < viewEdgesLength;) {
|
||||
canvas.gfx.moveTo(viewEdges[i++], viewEdges[i++]);
|
||||
canvas.gfx.lineTo(viewEdges[i++], viewEdges[i++]);
|
||||
}
|
||||
} else {
|
||||
if (occludeAll) {
|
||||
if (canvas == null) canvas = parentCanvas.getChildCanvas(true, false);
|
||||
canvas.gfx.lineStyle(6, 0xFF0000);
|
||||
canvas.gfx.moveTo(-camera.viewSizeX, -camera.viewSizeY);
|
||||
canvas.gfx.lineTo(-camera.viewSizeX, camera.viewSizeY);
|
||||
canvas.gfx.lineTo(camera.viewSizeX, camera.viewSizeY);
|
||||
canvas.gfx.lineTo(camera.viewSizeX, -camera.viewSizeY);
|
||||
canvas.gfx.lineTo(-camera.viewSizeX, -camera.viewSizeY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Оси, центры, имена, баунды
|
||||
if (inDebug & Debug.AXES) object.drawAxes(camera, canvas);
|
||||
if (inDebug & Debug.CENTERS) object.drawCenter(camera, canvas);
|
||||
if (inDebug & Debug.NAMES) object.drawName(camera, canvas);
|
||||
if (inDebug & Debug.BOUNDS) object.drawBoundBox(camera, canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
60
Alternativa3D7/7.1/alternativa/engine3d/objects/Reference.as
Normal file
60
Alternativa3D7/7.1/alternativa/engine3d/objects/Reference.as
Normal file
@@ -0,0 +1,60 @@
|
||||
package alternativa.engine3d.objects {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.geom.Matrix3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Объект-ссылка.
|
||||
* Может ссылаться на любой трёхмерный объект, в том числе контейнер с любой вложенностью или Reference.
|
||||
* При отрисовке он отрисовывает вместо себя объект,
|
||||
* на который ссылается, подставляя только свою трансформацию, alpha, blendMode, colorTransform и filters.
|
||||
*/
|
||||
public class Reference extends Object3D {
|
||||
|
||||
/**
|
||||
* Объект, который подставляется при отрисовке вместо себя
|
||||
*/
|
||||
public var referenceObject:Object3D;
|
||||
|
||||
public function Reference(referenceObject:Object3D = null) {
|
||||
this.referenceObject = referenceObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function get canDraw():Boolean {
|
||||
return referenceObject.canDraw;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
referenceObject.draw(camera, object, parentCanvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function debug(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
referenceObject.debug(camera, object, parentCanvas);
|
||||
}
|
||||
|
||||
override public function get boundBox():BoundBox {
|
||||
return referenceObject.boundBox;
|
||||
}
|
||||
|
||||
override public function calculateBoundBox(matrix:Matrix3D = null, boundBox:BoundBox = null):BoundBox {
|
||||
return referenceObject.calculateBoundBox(matrix, boundBox);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
148
Alternativa3D7/7.1/alternativa/engine3d/objects/SkeletalMesh.as
Normal file
148
Alternativa3D7/7.1/alternativa/engine3d/objects/SkeletalMesh.as
Normal file
@@ -0,0 +1,148 @@
|
||||
package alternativa.engine3d.objects {
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
|
||||
import flash.geom.Matrix3D;
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class SkeletalMesh extends Mesh {
|
||||
|
||||
alternativa3d var _numBones:uint = 0;
|
||||
alternativa3d var bones:Vector.<Bone> = new Vector.<Bone>();
|
||||
private var weights:Vector.<Vector.<Number>>;
|
||||
private var originalVertices:Vector.<Number>;
|
||||
private var originalBonesMatrices:Vector.<Matrix3D>;
|
||||
private var boneVertices:Vector.<Number>;
|
||||
|
||||
public function initBones():void {
|
||||
// Инициализируем массив весов и сохраняем оригинальные матрицы костей
|
||||
originalBonesMatrices = new Vector.<Matrix3D>(_numBones, true);
|
||||
weights = new Vector.<Vector.<Number>>(_numBones, true);
|
||||
for (var j:int = 0; j < _numBones; j++) {
|
||||
originalBonesMatrices[j] = new Matrix3D();
|
||||
originalBonesMatrices[j].prepend(bones[j].matrix);
|
||||
originalBonesMatrices[j].invert();
|
||||
weights[j] = new Vector.<Number>(numVertices, true);
|
||||
}
|
||||
// Формируем вспомогательные массивы для вершин
|
||||
originalVertices = new Vector.<Number>(numVertices*3, true);
|
||||
boneVertices = new Vector.<Number>(numVertices*3, true);
|
||||
|
||||
// Обрабатываем вершины
|
||||
var v:Vector3D = new Vector3D();
|
||||
for (var i:int = 0; i < numVertices; i++) {
|
||||
var k:int = i*3;
|
||||
// Сохраняем оригинальные координаты вершин
|
||||
originalVertices[k] = vertices[k];
|
||||
originalVertices[k + 1] = vertices[k + 1];
|
||||
originalVertices[k + 2] = vertices[k + 2];
|
||||
|
||||
// Находим веса для каждой кости
|
||||
var sumWeight:Number = 0;
|
||||
for (j = 0; j < _numBones; j++) {
|
||||
// Находим расстояние от вершины до кости
|
||||
var b1:Vector3D = bones[j].matrix.transformVector(new Vector3D());
|
||||
var b2:Vector3D = bones[j].matrix.transformVector(new Vector3D(0, 0, bones[j].length));
|
||||
v.x = originalVertices[k];
|
||||
v.y = originalVertices[k + 1];
|
||||
v.z = originalVertices[k + 2];
|
||||
var w:Number = 1 - distanceToBone(b1, b2, v)/bones[j].distance;
|
||||
//trace(w);
|
||||
w = (w > 0) ? w : 0;
|
||||
weights[j][i] = w;
|
||||
sumWeight += w;
|
||||
}
|
||||
|
||||
// Нормализуем веса
|
||||
if (sumWeight > 0) {
|
||||
for (j = 0; j < _numBones; j++) {
|
||||
weights[j][i] /= sumWeight;
|
||||
}
|
||||
} else {
|
||||
// Если вершина не относится ни к какой кости, помечаем
|
||||
for (j = 0; j < _numBones; j++) {
|
||||
weights[j][i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private function distanceToBone(b1:Vector3D, b2:Vector3D, p:Vector3D):Number {
|
||||
var v:Vector3D = b2.subtract(b1);
|
||||
var w:Vector3D = p.subtract(b1);
|
||||
|
||||
var c1:Number = w.dotProduct(v);
|
||||
if ( c1 <= 0 )
|
||||
return Vector3D.distance(p, b1);
|
||||
|
||||
var c2:Number = v.dotProduct(v);
|
||||
if ( c2 <= c1 )
|
||||
return Vector3D.distance(p, b2);
|
||||
|
||||
v.scaleBy(c1 / c2);
|
||||
var Pb:Vector3D = b1.add(v);
|
||||
return Vector3D.distance(p, Pb);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private var m:Matrix3D = new Matrix3D();
|
||||
public function calculateBones():void {
|
||||
// Обнуление координат
|
||||
for (var i:int = 0; i < numVertices*3; i++) {
|
||||
vertices[i] = 0;
|
||||
}
|
||||
// Добавление трансформации через кости
|
||||
for (var j:int = 0; j < _numBones; j++) {
|
||||
m.identity();
|
||||
m.prepend(bones[j].matrix);
|
||||
m.prepend(originalBonesMatrices[j]);
|
||||
m.transformVectors(originalVertices, boneVertices);
|
||||
var boneWeights:Vector.<Number> = weights[j];
|
||||
for (i = 0; i < numVertices; i++) {
|
||||
var weight:Number = boneWeights[i];
|
||||
var k1:int = i*3;
|
||||
var k2:int = k1 + 1;
|
||||
var k3:int = k1 + 2;
|
||||
if (weight >= 0) {
|
||||
vertices[k1] += boneVertices[k1]*weight;
|
||||
vertices[k2] += boneVertices[k2]*weight;
|
||||
vertices[k3] += boneVertices[k3]*weight;
|
||||
} else {
|
||||
vertices[k1] = originalVertices[k1];
|
||||
vertices[k2] = originalVertices[k2];
|
||||
vertices[k3] = originalVertices[k3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function addBone(bone:Bone):void {
|
||||
bones[_numBones++] = bone;
|
||||
}
|
||||
/*
|
||||
public function removeChild(bone:Bone):void {
|
||||
var i:int = bones.indexOf(bone);
|
||||
if (i < 0) throw new ArgumentError();
|
||||
children.splice(i, 1);
|
||||
_numBones--;
|
||||
var len:uint = drawChildren.length;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (drawChildren[i] == bone) {
|
||||
drawChildren.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
bone._parent = null;
|
||||
bone.setStage(null);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
470
Alternativa3D7/7.1/alternativa/engine3d/objects/Sprite3D.as
Normal file
470
Alternativa3D7/7.1/alternativa/engine3d/objects/Sprite3D.as
Normal file
@@ -0,0 +1,470 @@
|
||||
package alternativa.engine3d.objects {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.MipMap;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.display.BitmapData;
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Matrix3D;
|
||||
import alternativa.engine3d.core.Debug;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Плоский, всегда развёрнутый к камере трёхмерный объект
|
||||
*/
|
||||
public class Sprite3D extends Object3D {
|
||||
|
||||
static private const axes:Vector.<Number> = Vector.<Number>([0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1]);
|
||||
static private const cameraAxes:Vector.<Number> = new Vector.<Number>(12, true);
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
static alternativa3d const textureMatrix:Matrix = new Matrix();
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
static alternativa3d const vertices:Vector.<Number> = new Vector.<Number>();
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var drawTexture:BitmapData;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var projectionX:Number;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var projectionY:Number;
|
||||
|
||||
public var texture:BitmapData;
|
||||
public var mipMap:MipMap;
|
||||
/**
|
||||
* X точки привязки
|
||||
*/
|
||||
public var originX:Number = 0.5;
|
||||
/**
|
||||
* Y точки привязки
|
||||
*/
|
||||
public var originY:Number = 0.5;
|
||||
public var smooth:Boolean = false;
|
||||
/**
|
||||
* Режим отсечения объекта по пирамиде видимости камеры.
|
||||
* 0 - весь объект
|
||||
* 2 - клиппинг граней по пирамиде видимости камеры
|
||||
*/
|
||||
public var clipping:int = 0; // 0 - весь объект, 2 - с обрезкой
|
||||
public var mipMapping:int = 0; // 0 - без мипмаппинга, 1 - по дальности
|
||||
/**
|
||||
* Угол поворота в радианах в плоскости экрана
|
||||
*/
|
||||
public var rotation:Number = 0;
|
||||
/**
|
||||
* Зависимость размера на экране от удалённости от камеры
|
||||
*/
|
||||
public var perspectiveScale:Boolean = true;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
|
||||
var length:int;
|
||||
if ((length = calculateVertices(object, camera)) == 0) return;
|
||||
|
||||
// Подготовка канваса
|
||||
var canvas:Canvas = parentCanvas.getChildCanvas(true, false, object.alpha, object.blendMode, object.colorTransform, object.filters);
|
||||
|
||||
canvas.gfx.beginBitmapFill(drawTexture, textureMatrix, false, smooth);
|
||||
|
||||
if (rotation == 0) {
|
||||
// Простая отрисовка
|
||||
var x:Number = vertices[0]*projectionX, y:Number = vertices[1]*projectionY;
|
||||
canvas.gfx.drawRect(x, y, vertices[6]*projectionX - x, vertices[7]*projectionY - y);
|
||||
} else {
|
||||
// Отрисовка
|
||||
canvas.gfx.moveTo(vertices[length - 3]*projectionX, vertices[length - 2]*projectionY);
|
||||
for (var i:int = 0; i < length; i++) canvas.gfx.lineTo(vertices[i++]*projectionX, vertices[i++]*projectionY);
|
||||
}
|
||||
}
|
||||
|
||||
override alternativa3d function debug(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
var inDebug:int = camera.checkInDebug(this);
|
||||
if (inDebug == 0) return;
|
||||
var canvas:Canvas = parentCanvas.getChildCanvas(true, false);
|
||||
|
||||
var i:int, length:int, x:Number, y:Number, t:Number = 0.1;
|
||||
// Рёбра
|
||||
if (inDebug & Debug.EDGES) {
|
||||
if ((length = calculateVertices(object, camera)) > 0) {
|
||||
if (canvas == null) canvas = parentCanvas.getChildCanvas(true, false);
|
||||
canvas.gfx.lineStyle(0, 0xFFFFFF);
|
||||
if (rotation == 0) {
|
||||
x = vertices[0]*projectionX, y = vertices[1]*projectionY;
|
||||
canvas.gfx.drawRect(x, y, vertices[6]*projectionX - x, vertices[7]*projectionY - y);
|
||||
} else {
|
||||
// Отрисовка
|
||||
canvas.gfx.moveTo(vertices[length - 3]*projectionX, vertices[length - 2]*projectionY);
|
||||
for (i = 0; i < length; i++) canvas.gfx.lineTo(vertices[i++]*projectionX, vertices[i++]*projectionY);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Вершины
|
||||
if (inDebug & Debug.VERTICES) {
|
||||
if ((length = calculateVertices(object, camera)) > 0) {
|
||||
if (canvas == null) canvas = parentCanvas.getChildCanvas(true, false);
|
||||
canvas.gfx.lineStyle();
|
||||
if (rotation == 0) {
|
||||
var x1:Number = vertices[0]*projectionX, y1:Number = vertices[1]*projectionY;
|
||||
var x2:Number = vertices[6]*projectionX, y2:Number = vertices[7]*projectionY;
|
||||
if (x1 > -camera.viewSizeX + t && x1 < camera.viewSizeX - t && y1 > -camera.viewSizeY + t && y1 < camera.viewSizeY - t) {
|
||||
canvas.gfx.beginFill(0xFFFF00);
|
||||
canvas.gfx.drawCircle(x1, y1, 2);
|
||||
}
|
||||
if (x1 > -camera.viewSizeX + t && x1 < camera.viewSizeX - t && y2 > -camera.viewSizeY + t && y2 < camera.viewSizeY - t) {
|
||||
canvas.gfx.beginFill(0xFFFF00);
|
||||
canvas.gfx.drawCircle(x1, y2, 2);
|
||||
}
|
||||
if (x2 > -camera.viewSizeX + t && x2 < camera.viewSizeX - t && y2 > -camera.viewSizeY + t && y2 < camera.viewSizeY - t) {
|
||||
canvas.gfx.beginFill(0xFFFF00);
|
||||
canvas.gfx.drawCircle(x2, y2, 2);
|
||||
}
|
||||
if (x2 > -camera.viewSizeX + t && x2 < camera.viewSizeX - t && y1 > -camera.viewSizeY + t && y1 < camera.viewSizeY - t) {
|
||||
canvas.gfx.beginFill(0xFFFF00);
|
||||
canvas.gfx.drawCircle(x2, y1, 2);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < length; i++) {
|
||||
x = vertices[i++]*projectionX, y = vertices[i++]*projectionY;
|
||||
if (x > -camera.viewSizeX + t && x < camera.viewSizeX - t && y > -camera.viewSizeY + t && y < camera.viewSizeY - t) {
|
||||
canvas.gfx.beginFill(0xFFFF00);
|
||||
canvas.gfx.drawCircle(x, y, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Оси, центры, имена, баунды
|
||||
if (inDebug & Debug.AXES) object.drawAxes(camera, canvas);
|
||||
if (inDebug & Debug.CENTERS) object.drawCenter(camera, canvas);
|
||||
if (inDebug & Debug.NAMES) object.drawName(camera, canvas);
|
||||
if (inDebug & Debug.BOUNDS) object.drawBoundBox(camera, canvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function calculateVertices(object:Object3D, camera:Camera3D):int {
|
||||
|
||||
// Получение матрицы и позиции
|
||||
object.cameraMatrix.transformVectors(axes, cameraAxes);
|
||||
var cz:Number = cameraAxes[2];
|
||||
|
||||
if (cz < camera.nearClipping || cz > camera.farClipping) return 0;
|
||||
var cx:Number = cameraAxes[0];
|
||||
var cy:Number = cameraAxes[1];
|
||||
|
||||
// Нахождение среднего размера спрайта
|
||||
var ax:Number = (cameraAxes[3] - cx)*camera.invertPerspectiveScaleX;
|
||||
var ay:Number = (cameraAxes[4] - cy)*camera.invertPerspectiveScaleY;
|
||||
var az:Number = cameraAxes[5] - cz;
|
||||
var size:Number = Math.sqrt(ax*ax + ay*ay + az*az);
|
||||
ax = (cameraAxes[6] - cx)*camera.invertPerspectiveScaleX;
|
||||
ay = (cameraAxes[7] - cy)*camera.invertPerspectiveScaleY;
|
||||
az = cameraAxes[8] - cz;
|
||||
size += Math.sqrt(ax*ax + ay*ay + az*az);
|
||||
ax = (cameraAxes[9] - cx)*camera.invertPerspectiveScaleX;
|
||||
ay = (cameraAxes[10] - cy)*camera.invertPerspectiveScaleY;
|
||||
az = cameraAxes[11] - cz;
|
||||
size = (size + Math.sqrt(ax*ax + ay*ay + az*az))/3;
|
||||
|
||||
// Определение текстуры и коррекция размера
|
||||
var level:int;
|
||||
if (mipMapping == 0) {
|
||||
drawTexture = texture;
|
||||
} else {
|
||||
size *= Math.pow(2, level = mipMap.getLevel(cz/size, camera));
|
||||
drawTexture = mipMap.textures[level];
|
||||
}
|
||||
|
||||
// Проекция на экран
|
||||
var projectionZ:Number = camera.focalLength/cz;
|
||||
projectionX = camera.viewSizeX/cz;
|
||||
projectionY = camera.viewSizeY/cz;
|
||||
|
||||
if (!perspectiveScale) size /= projectionZ;
|
||||
|
||||
var x1:Number, y1:Number, x2:Number, y2:Number;
|
||||
|
||||
if (rotation == 0) {
|
||||
|
||||
// Размеры спрайта в матрице камеры
|
||||
var cameraWidth:Number = drawTexture.width*camera.perspectiveScaleX*size;
|
||||
var cameraHeight:Number = drawTexture.height*camera.perspectiveScaleY*size;
|
||||
|
||||
// Расчёт вершин в матрице камеры
|
||||
x1 = cx - originX*cameraWidth;
|
||||
y1 = cy - originY*cameraHeight;
|
||||
x2 = x1 + cameraWidth;
|
||||
y2 = y1 + cameraHeight;
|
||||
|
||||
// Отсечение по вьюпорту
|
||||
if (object.culling > 0 && (x1 > cz || y1 > cz || x2 < -cz || y2 < -cz)) return 0;
|
||||
|
||||
// Подготовка матрицы отрисовки
|
||||
textureMatrix.a = textureMatrix.d = size*projectionZ;
|
||||
textureMatrix.b = textureMatrix.c = 0;
|
||||
textureMatrix.tx = x1*projectionX;
|
||||
textureMatrix.ty = y1*projectionY;
|
||||
|
||||
// Подрезка
|
||||
if (clipping == 2) {
|
||||
if (x1 < -cz) x1 = -cz;
|
||||
if (y1 < -cz) y1 = -cz;
|
||||
if (x2 > cz) x2 = cz;
|
||||
if (y2 > cz) y2 = cz;
|
||||
}
|
||||
|
||||
// Заполняем вершины
|
||||
vertices[0] = x1;
|
||||
vertices[1] = y1;
|
||||
vertices[2] = cz;
|
||||
vertices[3] = x1;
|
||||
vertices[4] = y2;
|
||||
vertices[5] = cz;
|
||||
vertices[6] = x2;
|
||||
vertices[7] = y2;
|
||||
vertices[8] = cz;
|
||||
vertices[9] = x2;
|
||||
vertices[10] = y1;
|
||||
vertices[11] = cz;
|
||||
|
||||
return 12;
|
||||
|
||||
} else {
|
||||
|
||||
// Размер спрайта в камере без коррекции под FOV90
|
||||
var textureWidth:Number = drawTexture.width;
|
||||
var textureHeight:Number = drawTexture.height;
|
||||
|
||||
// Расчёт векторов ширины и высоты
|
||||
var sin:Number = Math.sin(rotation)*size;
|
||||
var cos:Number = Math.cos(rotation)*size;
|
||||
var cameraWidthX:Number = cos*textureWidth*camera.perspectiveScaleX;
|
||||
var cameraWidthY:Number = -sin*textureWidth*camera.perspectiveScaleY;
|
||||
var cameraHeightX:Number = sin*textureHeight*camera.perspectiveScaleX;
|
||||
var cameraHeightY:Number = cos*textureHeight*camera.perspectiveScaleY;
|
||||
|
||||
// Заполняем вершины
|
||||
var length:int = 12;
|
||||
vertices[0] = x1 = cx - originX*cameraWidthX - originY*cameraHeightX;
|
||||
vertices[1] = y1 = cy - originX*cameraWidthY - originY*cameraHeightY;
|
||||
vertices[2] = cz;
|
||||
vertices[3] = x1 + cameraHeightX;
|
||||
vertices[4] = y1 + cameraHeightY;
|
||||
vertices[5] = cz;
|
||||
vertices[6] = x1 + cameraWidthX + cameraHeightX;
|
||||
vertices[7] = y1 + cameraWidthY + cameraHeightY;
|
||||
vertices[8] = cz;
|
||||
vertices[9] = x1 + cameraWidthX;
|
||||
vertices[10] = y1 + cameraWidthY;
|
||||
vertices[11] = cz;
|
||||
|
||||
if (object.culling > 0) {
|
||||
// Отсечение по вьюпорту
|
||||
var i:int, infront:Boolean, behind:Boolean, inside:Boolean;
|
||||
var clipLeft:Boolean = false;
|
||||
if (object.culling & 4) {
|
||||
for (i = 0; i < length; i += 3) if ((inside = -vertices[i] < cz) && (infront = true) && behind || !inside && (behind = true) && infront) break;
|
||||
if (behind) if (!infront) return 0; else clipLeft = true;
|
||||
infront = false; behind = false;
|
||||
}
|
||||
var clipRight:Boolean = false;
|
||||
if (object.culling & 8) {
|
||||
for (i = 0; i < length; i += 3) if ((inside = vertices[i] < cz) && (infront = true) && behind || !inside && (behind = true) && infront) break;
|
||||
if (behind) if (!infront) return 0; else clipRight = true;
|
||||
infront = false; behind = false;
|
||||
}
|
||||
var clipTop:Boolean = false;
|
||||
if (object.culling & 16) {
|
||||
for (i = 1; i < length; i += 3) if ((inside = -vertices[i] < cz) && (infront = true) && behind || !inside && (behind = true) && infront) break;
|
||||
if (behind) if (!infront) return 0; else clipTop = true;
|
||||
infront = false; behind = false;
|
||||
}
|
||||
var clipBottom:Boolean = false;
|
||||
if (object.culling & 32) {
|
||||
for (i = 1; i < length; i += 3) if ((inside = vertices[i] < cz) && (infront = true) && behind || !inside && (behind = true) && infront) break;
|
||||
if (behind) if (!infront) return 0; else clipBottom = true;
|
||||
}
|
||||
// Подрезка
|
||||
if (clipping == 2) {
|
||||
var n:int = 0, t:Number, bx:Number, by:Number;
|
||||
if (clipLeft) {
|
||||
ax = cx = vertices[0]; ay = cy = vertices[1];
|
||||
for (i = 3; i <= length; i++) {
|
||||
if (i < length) {
|
||||
bx = vertices[i++]; by = vertices[i++];
|
||||
} else {
|
||||
bx = cx; by = cy;
|
||||
}
|
||||
if (-bx < cz && -ax >= cz || -bx >= cz && -ax < cz) {
|
||||
t = (ax + cz)/(ax - bx);
|
||||
vertices[n++] = ax + (bx - ax)*t;
|
||||
vertices[n++] = ay + (by - ay)*t;
|
||||
vertices[n++] = cz;
|
||||
}
|
||||
if (-bx < cz) {
|
||||
vertices[n++] = bx; vertices[n++] = by; vertices[n++] = cz;
|
||||
}
|
||||
ax = bx; ay = by;
|
||||
}
|
||||
if (n == 0) return 0;
|
||||
length = n; n = 0;
|
||||
}
|
||||
if (clipRight) {
|
||||
ax = cx = vertices[0]; ay = cy = vertices[1];
|
||||
for (i = 3; i <= length; i++) {
|
||||
if (i < length) {
|
||||
bx = vertices[i++]; by = vertices[i++];
|
||||
} else {
|
||||
bx = cx; by = cy;
|
||||
}
|
||||
if (bx < cz && ax >= cz || bx >= cz && ax < cz) {
|
||||
t = (cz - ax)/(bx - ax);
|
||||
vertices[n++] = ax + (bx - ax)*t;
|
||||
vertices[n++] = ay + (by - ay)*t;
|
||||
vertices[n++] = cz;
|
||||
}
|
||||
if (bx < cz) {
|
||||
vertices[n++] = bx; vertices[n++] = by; vertices[n++] = cz;
|
||||
}
|
||||
ax = bx; ay = by;
|
||||
}
|
||||
if (n == 0) return 0;
|
||||
length = n; n = 0;
|
||||
}
|
||||
if (clipTop) {
|
||||
ax = cx = vertices[0]; ay = cy = vertices[1];
|
||||
for (i = 3; i <= length; i++) {
|
||||
if (i < length) {
|
||||
bx = vertices[i++]; by = vertices[i++];
|
||||
} else {
|
||||
bx = cx; by = cy;
|
||||
}
|
||||
if (-by < cz && -ay >= cz || -by >= cz && -ay < cz) {
|
||||
t = (ay + cz)/(ay - by);
|
||||
vertices[n++] = ax + (bx - ax)*t;
|
||||
vertices[n++] = ay + (by - ay)*t;
|
||||
vertices[n++] = cz;
|
||||
}
|
||||
if (-by < cz) {
|
||||
vertices[n++] = bx; vertices[n++] = by; vertices[n++] = cz;
|
||||
}
|
||||
ax = bx; ay = by;
|
||||
}
|
||||
if (n == 0) return 0;
|
||||
length = n; n = 0;
|
||||
}
|
||||
if (clipBottom) {
|
||||
ax = cx = vertices[0]; ay = cy = vertices[1];
|
||||
for (i = 3; i <= length; i++) {
|
||||
if (i < length) {
|
||||
bx = vertices[i++]; by = vertices[i++];
|
||||
} else {
|
||||
bx = cx; by = cy;
|
||||
}
|
||||
if (by < cz && ay >= cz || by >= cz && ay < cz) {
|
||||
t = (cz - ay)/(by - ay);
|
||||
vertices[n++] = ax + (bx - ax)*t;
|
||||
vertices[n++] = ay + (by - ay)*t;
|
||||
vertices[n++] = cz;
|
||||
}
|
||||
if (by < cz) {
|
||||
vertices[n++] = bx; vertices[n++] = by; vertices[n++] = cz;
|
||||
}
|
||||
ax = bx; ay = by;
|
||||
}
|
||||
if (n == 0) return 0;
|
||||
length = n; n = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Подготовка матрицы отрисовки
|
||||
textureMatrix.a = textureMatrix.d = cos*projectionZ;
|
||||
textureMatrix.b = -sin*projectionZ;
|
||||
textureMatrix.c = sin*projectionZ;
|
||||
textureMatrix.tx = x1*projectionX;
|
||||
textureMatrix.ty = y1*projectionY;
|
||||
|
||||
return length;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override public function calculateBoundBox(matrix:Matrix3D = null, boundBox:BoundBox = null):BoundBox {
|
||||
// Если указан баунд-бокс
|
||||
if (boundBox != null) {
|
||||
boundBox.infinity();
|
||||
} else {
|
||||
boundBox = new BoundBox();
|
||||
}
|
||||
// Расчёт локального радиуса
|
||||
var t:BitmapData = (mipMapping == 0) ? texture : mipMap.textures[0];
|
||||
var w:Number = ((originX >= 0.5) ? originX : (1 - originX))*t.width;
|
||||
var h:Number = ((originY >= 0.5) ? originY : (1 - originY))*t.height;
|
||||
var radius:Number = Math.sqrt(w*w + h*h);
|
||||
// Если указана матрица трансформации, переводим
|
||||
if (matrix != null) {
|
||||
matrix.transformVectors(axes, cameraAxes);
|
||||
var cz:Number = cameraAxes[2];
|
||||
var cx:Number = cameraAxes[0];
|
||||
var cy:Number = cameraAxes[1];
|
||||
// Нахождение среднего размера спрайта
|
||||
var ax:Number = cameraAxes[3] - cx;
|
||||
var ay:Number = cameraAxes[4] - cy;
|
||||
var az:Number = cameraAxes[5] - cz;
|
||||
var size:Number = Math.sqrt(ax*ax + ay*ay + az*az);
|
||||
ax = cameraAxes[6] - cx;
|
||||
ay = cameraAxes[7] - cy;
|
||||
az = cameraAxes[8] - cz;
|
||||
size += Math.sqrt(ax*ax + ay*ay + az*az);
|
||||
ax = cameraAxes[9] - cx;
|
||||
ay = cameraAxes[10] - cy;
|
||||
az = cameraAxes[11] - cz;
|
||||
size = radius*(size + Math.sqrt(ax*ax + ay*ay + az*az))/3;
|
||||
boundBox.setSize(cx - size, cy - size, cz - size, cx + size, cy + size, cz + size);
|
||||
} else {
|
||||
boundBox.setSize(-radius, -radius, -radius, radius, radius, radius);
|
||||
}
|
||||
return boundBox;
|
||||
}
|
||||
|
||||
public function copyFrom(source:Sprite3D):void {
|
||||
visible = source.visible;
|
||||
alpha = source.alpha;
|
||||
blendMode = source.blendMode;
|
||||
originX = source.originX;
|
||||
originY = source.originY;
|
||||
smooth = source.smooth;
|
||||
clipping = source.clipping;
|
||||
rotation = source.rotation;
|
||||
perspectiveScale = source.perspectiveScale;
|
||||
texture = source.texture;
|
||||
mipMapping = source.mipMapping;
|
||||
mipMap = source.mipMap;
|
||||
matrix.identity();
|
||||
matrix.append(source.matrix);
|
||||
if (source.boundBox != null) {
|
||||
if (boundBox == null) boundBox = new BoundBox();
|
||||
boundBox.copyFrom(source.boundBox);
|
||||
} else boundBox = null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package alternativa.engine3d.objects {
|
||||
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.geom.Utils3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class WireBoundBox extends Object3D {
|
||||
|
||||
static private const cameraVertices:Vector.<Number> = new Vector.<Number>(24, true);
|
||||
static private const projectedVertices:Vector.<Number> = new Vector.<Number>(16, true);
|
||||
static private const uvts:Vector.<Number> = new Vector.<Number>(24, true);
|
||||
public var thickness:Number = 0;
|
||||
public var color:uint = 0xFFFFFF;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
|
||||
cameraVertices[0] = _boundBox.minX;
|
||||
cameraVertices[1] = _boundBox.minY;
|
||||
cameraVertices[2] = _boundBox.minZ;
|
||||
|
||||
cameraVertices[3] = _boundBox.minX;
|
||||
cameraVertices[4] = _boundBox.minY;
|
||||
cameraVertices[5] = _boundBox.maxZ;
|
||||
|
||||
cameraVertices[6] = _boundBox.minX;
|
||||
cameraVertices[7] = _boundBox.maxY;
|
||||
cameraVertices[8] = _boundBox.minZ;
|
||||
|
||||
cameraVertices[9] = _boundBox.minX;
|
||||
cameraVertices[10] = _boundBox.maxY;
|
||||
cameraVertices[11] = _boundBox.maxZ;
|
||||
|
||||
cameraVertices[12] = _boundBox.maxX;
|
||||
cameraVertices[13] = _boundBox.minY;
|
||||
cameraVertices[14] = _boundBox.minZ;
|
||||
|
||||
cameraVertices[15] = _boundBox.maxX;
|
||||
cameraVertices[16] = _boundBox.minY;
|
||||
cameraVertices[17] = _boundBox.maxZ;
|
||||
|
||||
cameraVertices[18] = _boundBox.maxX;
|
||||
cameraVertices[19] = _boundBox.maxY;
|
||||
cameraVertices[20] = _boundBox.minZ;
|
||||
|
||||
cameraVertices[21] = _boundBox.maxX;
|
||||
cameraVertices[22] = _boundBox.maxY;
|
||||
cameraVertices[23] = _boundBox.maxZ;
|
||||
|
||||
object.cameraMatrix.transformVectors(cameraVertices, cameraVertices);
|
||||
for (var i:int = 0; i < 8; i++) {
|
||||
if (cameraVertices[int(i*3 + 2)] <= 0) return;
|
||||
}
|
||||
|
||||
// Подготовка канваса
|
||||
var canvas:Canvas = parentCanvas.getChildCanvas(true, false, object.alpha, object.blendMode, object.colorTransform, object.filters);
|
||||
|
||||
// Проецируем точки
|
||||
Utils3D.projectVectors(camera.projectionMatrix, cameraVertices, projectedVertices, uvts);
|
||||
|
||||
// Отрисовка
|
||||
canvas.gfx.lineStyle(thickness, color);
|
||||
canvas.gfx.moveTo(projectedVertices[0], projectedVertices[1]);
|
||||
canvas.gfx.lineTo(projectedVertices[2], projectedVertices[3]);
|
||||
canvas.gfx.lineTo(projectedVertices[6], projectedVertices[7]);
|
||||
canvas.gfx.lineTo(projectedVertices[4], projectedVertices[5]);
|
||||
canvas.gfx.lineTo(projectedVertices[0], projectedVertices[1]);
|
||||
canvas.gfx.moveTo(projectedVertices[8], projectedVertices[9]);
|
||||
canvas.gfx.lineTo(projectedVertices[10], projectedVertices[11]);
|
||||
canvas.gfx.lineTo(projectedVertices[14], projectedVertices[15]);
|
||||
canvas.gfx.lineTo(projectedVertices[12], projectedVertices[13]);
|
||||
canvas.gfx.lineTo(projectedVertices[8], projectedVertices[9]);
|
||||
canvas.gfx.moveTo(projectedVertices[0], projectedVertices[1]);
|
||||
canvas.gfx.lineTo(projectedVertices[8], projectedVertices[9]);
|
||||
canvas.gfx.moveTo(projectedVertices[2], projectedVertices[3]);
|
||||
canvas.gfx.lineTo(projectedVertices[10], projectedVertices[11]);
|
||||
canvas.gfx.moveTo(projectedVertices[4], projectedVertices[5]);
|
||||
canvas.gfx.lineTo(projectedVertices[12], projectedVertices[13]);
|
||||
canvas.gfx.moveTo(projectedVertices[6], projectedVertices[7]);
|
||||
canvas.gfx.lineTo(projectedVertices[14], projectedVertices[15]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
48
Alternativa3D7/7.1/alternativa/engine3d/objects/WireQuad.as
Normal file
48
Alternativa3D7/7.1/alternativa/engine3d/objects/WireQuad.as
Normal file
@@ -0,0 +1,48 @@
|
||||
package alternativa.engine3d.objects {
|
||||
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
import flash.geom.Utils3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class WireQuad extends Object3D {
|
||||
|
||||
public var vertices:Vector.<Number>;
|
||||
static private const cameraVertices:Vector.<Number> = new Vector.<Number>(12, true);
|
||||
static private const projectedVertices:Vector.<Number> = new Vector.<Number>(8, true);
|
||||
static private const uvts:Vector.<Number> = new Vector.<Number>(12, true);
|
||||
public var thickness:Number = 0;
|
||||
public var color:uint = 0xFFFFFF;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
|
||||
object.cameraMatrix.transformVectors(vertices, cameraVertices);
|
||||
for (var i:int = 0; i < 4; i++) {
|
||||
if (cameraVertices[(i*3 + 2)] <= 0) return;
|
||||
}
|
||||
|
||||
// Подготовка канваса
|
||||
var canvas:Canvas = parentCanvas.getChildCanvas(true, false, object.alpha, object.blendMode, object.colorTransform, object.filters);
|
||||
|
||||
// Проецируем точки
|
||||
Utils3D.projectVectors(camera.projectionMatrix, cameraVertices, projectedVertices, uvts);
|
||||
|
||||
// Отрисовка
|
||||
canvas.gfx.lineStyle(thickness, color);
|
||||
canvas.gfx.moveTo(projectedVertices[0], projectedVertices[1]);
|
||||
canvas.gfx.lineTo(projectedVertices[2], projectedVertices[3]);
|
||||
canvas.gfx.lineTo(projectedVertices[4], projectedVertices[5]);
|
||||
canvas.gfx.lineTo(projectedVertices[6], projectedVertices[7]);
|
||||
canvas.gfx.lineTo(projectedVertices[0], projectedVertices[1]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
246
Alternativa3D7/7.1/alternativa/engine3d/primitives/Box.as
Normal file
246
Alternativa3D7/7.1/alternativa/engine3d/primitives/Box.as
Normal file
@@ -0,0 +1,246 @@
|
||||
package alternativa.engine3d.primitives {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class Box extends Mesh {
|
||||
|
||||
public function Box(width:Number = 100, length:Number = 100, height:Number = 100, widthSegments:uint = 1, lengthSegments:uint = 1, heightSegments:uint = 1, reverse:Boolean = false) {
|
||||
|
||||
var wp:uint = widthSegments + 1;
|
||||
var lp:uint = lengthSegments + 1;
|
||||
var hp:uint = heightSegments + 1;
|
||||
|
||||
createEmptyGeometry((wp*(lp + hp) + lp*hp) << 1, (widthSegments*(lengthSegments + heightSegments) + lengthSegments*heightSegments) << 2);
|
||||
|
||||
var wh:Number = width*0.5;
|
||||
var lh:Number = length*0.5;
|
||||
var hh:Number = height*0.5;
|
||||
var wd:Number = 1/widthSegments;
|
||||
var ld:Number = 1/lengthSegments;
|
||||
var hd:Number = 1/heightSegments;
|
||||
var ws:Number = width/widthSegments;
|
||||
var ls:Number = length/lengthSegments;
|
||||
var hs:Number = height/heightSegments;
|
||||
var x:uint;
|
||||
var y:uint;
|
||||
var z:uint;
|
||||
|
||||
var v:uint = 0;
|
||||
var u:uint = 0;
|
||||
var f:uint = 0;
|
||||
|
||||
// Нижняя грань
|
||||
for (x = 0; x < wp; x++) {
|
||||
for (y = 0; y < lp; y++) {
|
||||
vertices[v] = x*ws - wh;
|
||||
uvts[v] = (widthSegments - x)*wd;
|
||||
uvs[u++] = uvts[v++];
|
||||
vertices[v] = y*ls - lh;
|
||||
uvts[v] = (lengthSegments - y)*ld;
|
||||
uvs[u++] = uvts[v++];
|
||||
vertices[v++] = -hh;
|
||||
|
||||
if (x < widthSegments && y < lengthSegments) {
|
||||
if (reverse) {
|
||||
indices[f++] = (x + 1)*lp + y + 1;
|
||||
indices[f++] = x*lp + y + 1;
|
||||
indices[f++] = x*lp + y;
|
||||
|
||||
indices[f++] = (x + 1)*lp + y;
|
||||
indices[f++] = (x + 1)*lp + y + 1;
|
||||
indices[f++] = x*lp + y;
|
||||
} else {
|
||||
indices[f++] = x*lp + y;
|
||||
indices[f++] = (x + 1)*lp + y + 1;
|
||||
indices[f++] = (x + 1)*lp + y;
|
||||
|
||||
indices[f++] = x*lp + y;
|
||||
indices[f++] = x*lp + y + 1;
|
||||
indices[f++] = (x + 1)*lp + y + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var o:uint = wp*lp;
|
||||
|
||||
// Верхняя грань
|
||||
for (x = 0; x < wp; x++) {
|
||||
for (y = 0; y < lp; y++) {
|
||||
vertices[v] = x*ws - wh;
|
||||
uvts[v] = x*wd;
|
||||
uvs[u++] = uvts[v++];
|
||||
vertices[v] = y*ls - lh;
|
||||
uvts[v] = (lengthSegments - y)*ld;
|
||||
uvs[u++] = uvts[v++];
|
||||
vertices[v++] = hh;
|
||||
|
||||
if (x < widthSegments && y < lengthSegments) {
|
||||
if (reverse) {
|
||||
indices[f++] = o + x*lp + y + 1;
|
||||
indices[f++] = o + (x + 1)*lp + y + 1;
|
||||
indices[f++] = o + x*lp + y;
|
||||
|
||||
indices[f++] = o + (x + 1)*lp + y + 1;
|
||||
indices[f++] = o + (x + 1)*lp + y;
|
||||
indices[f++] = o + x*lp + y;
|
||||
} else {
|
||||
indices[f++] = o + x*lp + y;
|
||||
indices[f++] = o + (x + 1)*lp + y;
|
||||
indices[f++] = o + (x + 1)*lp + y + 1;
|
||||
|
||||
indices[f++] = o + x*lp + y;
|
||||
indices[f++] = o + (x + 1)*lp + y + 1;
|
||||
indices[f++] = o + x*lp + y + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
o += wp*lp;
|
||||
|
||||
// Передняя грань
|
||||
for (x = 0; x < wp; x++) {
|
||||
for (z = 0; z < hp; z++) {
|
||||
vertices[v] = x*ws - wh;
|
||||
uvts[v] = x*wd;
|
||||
uvs[u++] = uvts[v++];
|
||||
vertices[v] = -lh;
|
||||
uvts[v] = (heightSegments - z)*hd;
|
||||
uvs[u++] = uvts[v++];
|
||||
vertices[v++] = z*hs - hh;
|
||||
|
||||
if (x < widthSegments && z < heightSegments) {
|
||||
if (reverse) {
|
||||
indices[f++] = o + x*hp + z + 1;
|
||||
indices[f++] = o + (x + 1)*hp + z + 1;
|
||||
indices[f++] = o + x*hp + z;
|
||||
|
||||
indices[f++] = o + (x + 1)*hp + z + 1;
|
||||
indices[f++] = o + (x + 1)*hp + z;
|
||||
indices[f++] = o + x*hp + z;
|
||||
} else {
|
||||
indices[f++] = o + x*hp + z;
|
||||
indices[f++] = o + (x + 1)*hp + z;
|
||||
indices[f++] = o + (x + 1)*hp + z + 1;
|
||||
|
||||
indices[f++] = o + x*hp + z;
|
||||
indices[f++] = o + (x + 1)*hp + z + 1;
|
||||
indices[f++] = o + x*hp + z + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
o += wp*hp;
|
||||
|
||||
// Задняя грань
|
||||
for (x = 0; x < wp; x++) {
|
||||
for (z = 0; z < hp; z++) {
|
||||
vertices[v] = x*ws - wh;
|
||||
uvts[v] = (widthSegments - x)*wd;
|
||||
uvs[u++] = uvts[v++];
|
||||
vertices[v] = lh;
|
||||
uvts[v] = (heightSegments - z)*hd;
|
||||
uvs[u++] = uvts[v++];
|
||||
vertices[v++] = z*hs - hh;
|
||||
|
||||
if (x < widthSegments && z < heightSegments) {
|
||||
if (reverse) {
|
||||
indices[f++] = o + (x + 1)*hp + z;
|
||||
indices[f++] = o + (x + 1)*hp + z + 1;
|
||||
indices[f++] = o + x*hp + z + 1;
|
||||
|
||||
indices[f++] = o + (x + 1)*hp + z;
|
||||
indices[f++] = o + x*hp + z + 1;
|
||||
indices[f++] = o + x*hp + z;
|
||||
} else {
|
||||
indices[f++] = o + x*hp + z;
|
||||
indices[f++] = o + x*hp + z + 1;
|
||||
indices[f++] = o + (x + 1)*hp + z;
|
||||
|
||||
indices[f++] = o + x*hp + z + 1;
|
||||
indices[f++] = o + (x + 1)*hp + z + 1;
|
||||
indices[f++] = o + (x + 1)*hp + z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
o += wp*hp;
|
||||
|
||||
// Левая грань
|
||||
for (y = 0; y < lp; y++) {
|
||||
for (z = 0; z < hp; z++) {
|
||||
vertices[v] = -wh;
|
||||
uvts[v] = (lengthSegments - y)*ld;
|
||||
uvs[u++] = uvts[v++];
|
||||
vertices[v] = y*ls - lh;
|
||||
uvts[v] = (heightSegments - z)*hd;
|
||||
uvs[u++] = uvts[v++];
|
||||
vertices[v++] = z*hs - hh;
|
||||
|
||||
if (y < lengthSegments && z < heightSegments) {
|
||||
if (reverse) {
|
||||
indices[f++] = o + (y + 1)*hp + z;
|
||||
indices[f++] = o + (y + 1)*hp + z + 1;
|
||||
indices[f++] = o + y*hp + z + 1;
|
||||
|
||||
indices[f++] = o + (y + 1)*hp + z;
|
||||
indices[f++] = o + y*hp + z + 1;
|
||||
indices[f++] = o + y*hp + z;
|
||||
} else {
|
||||
indices[f++] = o + y*hp + z;
|
||||
indices[f++] = o + y*hp + z + 1;
|
||||
indices[f++] = o + (y + 1)*hp + z;
|
||||
|
||||
indices[f++] = o + y*hp + z + 1;
|
||||
indices[f++] = o + (y + 1)*hp + z + 1;
|
||||
indices[f++] = o + (y + 1)*hp + z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
o += lp*hp;
|
||||
|
||||
// Правая грань
|
||||
for (y = 0; y < lp; y++) {
|
||||
for (z = 0; z < hp; z++) {
|
||||
vertices[v] = wh;
|
||||
uvts[v] = y*ld;
|
||||
uvs[u++] = uvts[v++];
|
||||
vertices[v] = y*ls - lh;
|
||||
uvts[v] = (heightSegments - z)*hd;
|
||||
uvs[u++] = uvts[v++];
|
||||
vertices[v++] = z*hs - hh;
|
||||
|
||||
if (y < lengthSegments && z < heightSegments) {
|
||||
if (reverse) {
|
||||
indices[f++] = o + y*hp + z + 1;
|
||||
indices[f++] = o + (y + 1)*hp + z + 1;
|
||||
indices[f++] = o + y*hp + z;
|
||||
|
||||
indices[f++] = o + (y + 1)*hp + z + 1;
|
||||
indices[f++] = o + (y + 1)*hp + z;
|
||||
indices[f++] = o + y*hp + z;
|
||||
} else {
|
||||
indices[f++] = o + y*hp + z;
|
||||
indices[f++] = o + (y + 1)*hp + z;
|
||||
indices[f++] = o + (y + 1)*hp + z + 1;
|
||||
|
||||
indices[f++] = o + y*hp + z;
|
||||
indices[f++] = o + (y + 1)*hp + z + 1;
|
||||
indices[f++] = o + y*hp + z + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Установка границ
|
||||
_boundBox = new BoundBox();
|
||||
_boundBox.setSize(-wh, -lh, -hh, wh, lh, hh);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
246
Alternativa3D7/7.1/alternativa/engine3d/primitives/GeoSphere.as
Normal file
246
Alternativa3D7/7.1/alternativa/engine3d/primitives/GeoSphere.as
Normal file
@@ -0,0 +1,246 @@
|
||||
package alternativa.engine3d.primitives {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
public class GeoSphere extends Mesh {
|
||||
|
||||
public function GeoSphere(radius:Number = 100, segments:uint = 2, reverse:Boolean = false) {
|
||||
|
||||
const sections:uint = 20;
|
||||
|
||||
var i:uint;
|
||||
|
||||
var theta:Number;
|
||||
var sin:Number;
|
||||
var cos:Number;
|
||||
// z расстояние до нижней и верхней крышки полюса
|
||||
var subz:Number = 4.472136E-001*radius;
|
||||
// радиус на расстоянии subz
|
||||
var subrad:Number = 2*subz;
|
||||
|
||||
var v:uint = 0;
|
||||
|
||||
var f:uint = sections*segments*segments;
|
||||
createEmptyGeometry(f/2 + 2, f);
|
||||
|
||||
vertices[v++] = 0;
|
||||
vertices[v++] = 0;
|
||||
vertices[v++] = radius;
|
||||
|
||||
// Создание вершин верхней крышки
|
||||
for (i = 0; i < 5; i++) {
|
||||
theta = Math.PI*2*i/5;
|
||||
sin = Math.sin(theta);
|
||||
cos = Math.cos(theta);
|
||||
vertices[v++] = subrad*cos;
|
||||
vertices[v++] = subrad*sin;
|
||||
vertices[v++] = subz;
|
||||
}
|
||||
// Создание вершин нижней крышки
|
||||
for (i = 0; i < 5; i++) {
|
||||
theta = Math.PI*((i << 1) + 1)/5;
|
||||
sin = Math.sin(theta);
|
||||
cos = Math.cos(theta);
|
||||
vertices[v++] = subrad*cos;
|
||||
vertices[v++] = subrad*sin;
|
||||
vertices[v++] = -subz;
|
||||
}
|
||||
|
||||
vertices[v++] = 0;
|
||||
vertices[v++] = 0;
|
||||
vertices[v++] = -radius;
|
||||
|
||||
for (i = 1; i < 6; i++) {
|
||||
v = interpolate(0, i, segments, v);
|
||||
}
|
||||
for (i = 1; i < 6; i++) {
|
||||
v = interpolate(i, i % 5 + 1, segments, v);
|
||||
}
|
||||
for (i = 1; i < 6; i++) {
|
||||
v = interpolate(i, i + 5, segments, v);
|
||||
}
|
||||
for (i = 1; i < 6; i++) {
|
||||
v = interpolate(i, (i + 3) % 5 + 6, segments, v);
|
||||
}
|
||||
for (i = 1; i < 6; i++) {
|
||||
v = interpolate(i + 5, i % 5 + 6, segments, v);
|
||||
}
|
||||
for (i = 6; i < 11; i++) {
|
||||
v = interpolate(11, i, segments, v);
|
||||
}
|
||||
for (f = 0; f < 5; f++) {
|
||||
for (i = 1; i <= segments - 2; i++) {
|
||||
v = interpolate(12 + f*(segments - 1) + i, 12 + (f + 1) % 5*(segments - 1) + i, i + 1, v);
|
||||
}
|
||||
}
|
||||
for (f = 0; f < 5; f++) {
|
||||
for (i = 1; i <= segments - 2; i++) {
|
||||
v = interpolate(12 + (f + 15)*(segments - 1) + i, 12 + (f + 10)*(segments - 1) + i, i + 1, v);
|
||||
}
|
||||
}
|
||||
for (f = 0; f < 5; f++) {
|
||||
for (i = 1; i <= segments - 2; i++) {
|
||||
v = interpolate(12 + ((f + 1) % 5 + 15)*(segments - 1) + segments - 2 - i, 12 + (f + 10)*(segments - 1) + segments - 2 - i, i + 1, v);
|
||||
}
|
||||
}
|
||||
for (f = 0; f < 5; f++) {
|
||||
for (i = 1; i <= segments - 2; i++) {
|
||||
v = interpolate(12 + ((f + 1) % 5 + 25)*(segments - 1) + i, 12 + (f + 25)*(segments - 1) + i, i + 1, v);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < numVertices; i++) {
|
||||
var j:uint = i*3;
|
||||
uvts[j] = Math.atan2(vertices[j + 1], vertices[j])/(Math.PI*2);
|
||||
uvts[j] = 0.5 + (reverse ? -uvts[j] : uvts[j]);
|
||||
uvts[j + 1] = 0.5 + Math.asin(vertices[j + 2]/radius)/Math.PI;
|
||||
uvs[i << 1] = uvts[j];
|
||||
uvs[(i << 1) + 1] = uvts[j + 1];
|
||||
}
|
||||
|
||||
var num:uint = 0;
|
||||
for (f = 0; f <= sections - 1; f++) {
|
||||
for (var row:uint = 0; row <= segments - 1; row++) {
|
||||
for (var column:uint = 0; column <= row; column++) {
|
||||
var a:uint = findVertices(segments, f, row, column);
|
||||
var b:uint = findVertices(segments, f, row + 1, column);
|
||||
var c:uint = findVertices(segments, f, row + 1, column + 1);
|
||||
|
||||
if (reverse) {
|
||||
indices[num++] = a;
|
||||
indices[num++] = c;
|
||||
indices[num++] = b;
|
||||
} else {
|
||||
indices[num++] = a;
|
||||
indices[num++] = b;
|
||||
indices[num++] = c;
|
||||
}
|
||||
|
||||
if (column < row) {
|
||||
var d:uint = findVertices(segments, f, row, column + 1);
|
||||
if (reverse) {
|
||||
indices[num++] = a;
|
||||
indices[num++] = d;
|
||||
indices[num++] = c;
|
||||
} else {
|
||||
indices[num++] = a;
|
||||
indices[num++] = c;
|
||||
indices[num++] = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Установка границ
|
||||
_boundBox = new BoundBox();
|
||||
_boundBox.setSize(-radius, -radius, -radius, radius, radius, radius);
|
||||
}
|
||||
|
||||
private function interpolate(a:uint, b:uint, num:uint, v:uint):uint {
|
||||
if (num < 2) {
|
||||
return v;
|
||||
}
|
||||
a *= 3;
|
||||
b *= 3;
|
||||
var ax:Number = vertices[a];
|
||||
var ay:Number = vertices[a + 1];
|
||||
var az:Number = vertices[a + 2];
|
||||
var bx:Number = vertices[b];
|
||||
var by:Number = vertices[b + 1];
|
||||
var bz:Number = vertices[b + 2];
|
||||
var cos:Number = (ax*bx + ay*by + az*bz)/(ax*ax + ay*ay + az*az);
|
||||
cos = (cos < -1) ? -1 : ((cos > 1) ? 1 : cos);
|
||||
var theta:Number = Math.acos(cos);
|
||||
var sin:Number = Math.sin(theta);
|
||||
for (var e:uint = 1; e < num; e++) {
|
||||
var theta1:Number = theta*e/num;
|
||||
var theta2:Number = theta*(num - e)/num;
|
||||
var st1:Number = Math.sin(theta1);
|
||||
var st2:Number = Math.sin(theta2);
|
||||
vertices[v++] = (ax*st2 + bx*st1)/sin;
|
||||
vertices[v++] = (ay*st2 + by*st1)/sin;
|
||||
vertices[v++] = (az*st2 + bz*st1)/sin;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
private function findVertices(segments:uint, section:uint, row:uint, column:uint):uint {
|
||||
if (row == 0) {
|
||||
if (section < 5) {
|
||||
return (0);
|
||||
}
|
||||
if (section > 14) {
|
||||
return (11);
|
||||
}
|
||||
return (section - 4);
|
||||
}
|
||||
if (row == segments && column == 0) {
|
||||
if (section < 5) {
|
||||
return (section + 1);
|
||||
}
|
||||
if (section < 10) {
|
||||
return ((section + 4) % 5 + 6);
|
||||
}
|
||||
if (section < 15) {
|
||||
return ((section + 1) % 5 + 1);
|
||||
}
|
||||
return ((section + 1) % 5 + 6);
|
||||
}
|
||||
if (row == segments && column == segments) {
|
||||
if (section < 5) {
|
||||
return ((section + 1) % 5 + 1);
|
||||
}
|
||||
if (section < 10) {
|
||||
return (section + 1);
|
||||
}
|
||||
if (section < 15) {
|
||||
return (section - 9);
|
||||
}
|
||||
return (section - 9);
|
||||
}
|
||||
if (row == segments) {
|
||||
if (section < 5) {
|
||||
return (12 + (5 + section)*(segments - 1) + column - 1);
|
||||
}
|
||||
if (section < 10) {
|
||||
return (12 + (20 + (section + 4) % 5)*(segments - 1) + column - 1);
|
||||
}
|
||||
if (section < 15) {
|
||||
return (12 + (section - 5)*(segments - 1) + segments - 1 - column);
|
||||
}
|
||||
return (12 + (5 + section)*(segments - 1) + segments - 1 - column);
|
||||
}
|
||||
if (column == 0) {
|
||||
if (section < 5) {
|
||||
return (12 + section*(segments - 1) + row - 1);
|
||||
}
|
||||
if (section < 10) {
|
||||
return (12 + (section % 5 + 15)*(segments - 1) + row - 1);
|
||||
}
|
||||
if (section < 15) {
|
||||
return (12 + ((section + 1) % 5 + 15)*(segments - 1) + segments - 1 - row);
|
||||
}
|
||||
return (12 + ((section + 1) % 5 + 25)*(segments - 1) + row - 1);
|
||||
}
|
||||
if (column == row) {
|
||||
if (section < 5) {
|
||||
return (12 + (section + 1) % 5*(segments - 1) + row - 1);
|
||||
}
|
||||
if (section < 10) {
|
||||
return (12 + (section % 5 + 10)*(segments - 1) + row - 1);
|
||||
}
|
||||
if (section < 15) {
|
||||
return (12 + (section % 5 + 10)*(segments - 1) + segments - row - 1);
|
||||
}
|
||||
return (12 + (section % 5 + 25)*(segments - 1) + row - 1);
|
||||
}
|
||||
return (12 + 30*(segments - 1) + section*(segments - 1)*(segments - 2)/2 + (row - 1)*(row - 2)/2 + column - 1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
70
Alternativa3D7/7.1/alternativa/engine3d/primitives/Plane.as
Normal file
70
Alternativa3D7/7.1/alternativa/engine3d/primitives/Plane.as
Normal file
@@ -0,0 +1,70 @@
|
||||
package alternativa.engine3d.primitives {
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.BoundBox;
|
||||
import alternativa.engine3d.objects.Mesh;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Plane extends Mesh {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param width
|
||||
* @param length
|
||||
* @param widthSegments
|
||||
* @param lengthSegments
|
||||
*/
|
||||
public function Plane(width:Number = 100, length:Number = 100, widthSegments:uint = 1, lengthSegments:uint = 1) {
|
||||
|
||||
var wp:uint = widthSegments + 1;
|
||||
var lp:uint = lengthSegments + 1;
|
||||
|
||||
createEmptyGeometry(wp*lp, (widthSegments*lengthSegments) << 2);
|
||||
|
||||
var wh:Number = width*0.5;
|
||||
var lh:Number = length*0.5;
|
||||
var wd:Number = 1/widthSegments;
|
||||
var ld:Number = 1/lengthSegments;
|
||||
var ws:Number = width/widthSegments;
|
||||
var ls:Number = length/lengthSegments;
|
||||
var x:uint;
|
||||
var y:uint;
|
||||
var z:uint;
|
||||
|
||||
var v:uint = 0;
|
||||
var u:uint = 0;
|
||||
var f:uint = 0;
|
||||
|
||||
// Верхняя грань
|
||||
for (x = 0; x < wp; x++) {
|
||||
for (y = 0; y < lp; y++) {
|
||||
vertices[v] = x*ws - wh;
|
||||
uvts[v] = x*wd;
|
||||
uvs[u++] = uvts[v++];
|
||||
vertices[v] = y*ls - lh;
|
||||
uvts[v] = (lengthSegments - y)*ld;
|
||||
uvs[u++] = uvts[v++];
|
||||
vertices[v++] = 0;
|
||||
|
||||
if (x < widthSegments && y < lengthSegments) {
|
||||
indices[f++] = x*lp + y;
|
||||
indices[f++] = (x + 1)*lp + y;
|
||||
indices[f++] = (x + 1)*lp + y + 1;
|
||||
|
||||
indices[f++] = x*lp + y;
|
||||
indices[f++] = (x + 1)*lp + y + 1;
|
||||
indices[f++] = x*lp + y + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Установка границ
|
||||
_boundBox = new BoundBox();
|
||||
_boundBox.setSize(-wh, -lh, 0, wh, lh, 0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
14
Alternativa3D7/7.2/alternativa/Alternativa3D.as
Normal file
14
Alternativa3D7/7.2/alternativa/Alternativa3D.as
Normal file
@@ -0,0 +1,14 @@
|
||||
package alternativa {
|
||||
|
||||
/**
|
||||
* Класс содержит информацию о версии библиотеки.
|
||||
* Также используется для интеграции библиотеки в среду разработки Adobe Flash.
|
||||
*/
|
||||
public class Alternativa3D {
|
||||
|
||||
/**
|
||||
* Версия библиотеки в формате: поколение.feature-версия.fix-версия
|
||||
*/
|
||||
public static const version:String = "7.0.0";
|
||||
}
|
||||
}
|
||||
3
Alternativa3D7/7.2/alternativa/engine3d/alternativa3d.as
Normal file
3
Alternativa3D7/7.2/alternativa/engine3d/alternativa3d.as
Normal file
@@ -0,0 +1,3 @@
|
||||
package alternativa.engine3d {
|
||||
public namespace alternativa3d = "http://alternativaplatform.com/en/alternativa3d";
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package alternativa.engine3d.containers {
|
||||
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.engine3d.alternativa3d;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Canvas;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.core.Object3DContainer;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Контейнер, дочерние объекты которого отрисовываются по удалённости от камеры
|
||||
*/
|
||||
public class AverageZContainer extends Object3DContainer {
|
||||
|
||||
static private const averageZ:Vector.<Number> = new Vector.<Number>();
|
||||
static private const center:Vector.<Number> = Vector.<Number>([0, 0, 0]);
|
||||
static private const cameraCenter:Vector.<Number> = new Vector.<Number>(3, true);
|
||||
static private const sortingStack:Vector.<int> = new Vector.<int>();
|
||||
|
||||
override protected function drawVisibleChildren(camera:Camera3D, object:Object3D, canvas:Canvas):void {
|
||||
var i:int;
|
||||
var j:int;
|
||||
var l:int = 0;
|
||||
var r:int = numVisibleChildren - 1;
|
||||
var child:Object3D;
|
||||
var sortingStackIndex:int;
|
||||
var sortingLeft:Number;
|
||||
var sortingMedian:Number;
|
||||
var sortingRight:Number;
|
||||
var sortingChild:Object3D;
|
||||
// Сортировка
|
||||
for (i = 0; i < numVisibleChildren; i++) {
|
||||
child = visibleChildren[i];
|
||||
child.cameraMatrix.transformVectors(center, cameraCenter);
|
||||
averageZ[i] = cameraCenter[0]*cameraCenter[0] + cameraCenter[1]*cameraCenter[1] + cameraCenter[2]*cameraCenter[2];
|
||||
}
|
||||
sortingStack[0] = l;
|
||||
sortingStack[1] = r;
|
||||
sortingStackIndex = 2;
|
||||
while (sortingStackIndex > 0) {
|
||||
j = r = sortingStack[--sortingStackIndex];
|
||||
i = l = sortingStack[--sortingStackIndex];
|
||||
sortingMedian = averageZ[(r + l) >> 1];
|
||||
do {
|
||||
while ((sortingLeft = averageZ[i]) > sortingMedian) i++;
|
||||
while ((sortingRight = averageZ[j]) < sortingMedian) j--;
|
||||
if (i <= j) {
|
||||
sortingChild = visibleChildren[i];
|
||||
visibleChildren[i] = visibleChildren[j];
|
||||
visibleChildren[j] = sortingChild;
|
||||
averageZ[i++] = sortingRight;
|
||||
averageZ[j--] = sortingLeft;
|
||||
}
|
||||
} while (i <= j);
|
||||
if (l < j) {
|
||||
sortingStack[sortingStackIndex++] = l;
|
||||
sortingStack[sortingStackIndex++] = j;
|
||||
}
|
||||
if (i < r) {
|
||||
sortingStack[sortingStackIndex++] = i;
|
||||
sortingStack[sortingStackIndex++] = r;
|
||||
}
|
||||
}
|
||||
// Отрисовка
|
||||
for (i = numVisibleChildren - 1; i >= 0; i--) {
|
||||
child = visibleChildren[i];
|
||||
if (camera.debugMode) child.debug(camera, child, canvas);
|
||||
child.draw(camera, child, canvas);
|
||||
visibleChildren[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user