package alternativa.engine3d.loaders {
import alternativa.engine3d.*;
import alternativa.engine3d.core.Face;
import alternativa.engine3d.core.Mesh;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Surface;
import alternativa.engine3d.core.Vertex;
import alternativa.engine3d.materials.FillMaterial;
import alternativa.engine3d.materials.TextureMaterial;
import alternativa.engine3d.materials.TextureMaterialPrecision;
import alternativa.engine3d.materials.WireMaterial;
import alternativa.types.Matrix3D;
import alternativa.types.Point3D;
import alternativa.types.Texture;
import alternativa.utils.ColorUtils;
import alternativa.utils.MathUtils;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.Loader;
import flash.errors.IOError;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.utils.ByteArray;
import flash.utils.Endian;
use namespace alternativa3d;
/**
* Загрузчик моделей в формате 3DS.
*/
public class Loader3DS extends EventDispatcher {
/**
* Дюймы
*/
public static const INCHES:Number = 1;
/**
* Футы
*/
public static const FEET:Number = 0.0833333334;
/**
* Мили
*/
public static const MILES:Number = 0.0000157828;
/**
* Миллиметры
*/
public static const MILLIMETERS:Number = 25.4000000259;
/**
* Сантиметры
*/
public static const CENTIMETERS:Number = 2.5400000025;
/**
* Метры
*/
public static const METERS:Number = 0.0254;
/**
* Километры
*/
public static const KILOMETERS:Number = 0.0000254;
private static var stub:BitmapData;
private var _content:Object3D;
private var version:uint;
private var objectDatas:Array;
private var animationDatas:Array;
private var materialDatas:Array;
private var bitmaps:Array;
private var urlLoader:URLLoader;
private var data:ByteArray;
private var counter:uint;
private var sequence:Array;
private var loader:Loader;
private var context:LoaderContext;
private var path:String;
/**
* Повтор текстуры при заливке
*/
public var repeat:Boolean = true;
/**
* Сглаживание текстур при масштабировании
*/
public var smooth:Boolean = false;
/**
* Режим наложения цветов
*/
public var blendMode:String = BlendMode.NORMAL;
/**
* Точность перспективной коррекции
*/
public var precision:Number = TextureMaterialPrecision.MEDIUM;
/**
* Определяет в какие единицы измерения перевести координаты модели.
* Может быть любым значением из констант Loader3DS
*
* INCHES | переводить в дюймы |
* FEET | переводить в футы |
* MILES | переводить в мили |
* MILLIMETERS | переводить в миллиметры |
* CENTIMETERS | переводить в сантиметры |
* METERS | переводить в метры |
* KILOMETERS | переводить в километры |
*
*/
public var units:Number = INCHES;
/**
* Уровень мобильности
*/
public var mobility:int = 0;
/**
* Загрузка модели.
*
* @param url адрес, откуда загружается модель
* @param context
*/
public function load(url:String, context:LoaderContext = null):void {
path = url.substring(0, url.lastIndexOf("/") + 1);
this.context = context;
urlLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.load(new URLRequest(url));
urlLoader.addEventListener(Event.COMPLETE, on3DSLoad);
urlLoader.addEventListener(IOErrorEvent.IO_ERROR, on3DSError);
urlLoader.addEventListener(IOErrorEvent.NETWORK_ERROR, on3DSError);
urlLoader.addEventListener(IOErrorEvent.VERIFY_ERROR, on3DSError);
// Очистка
_content = null;
version = 0;
objectDatas = null;
animationDatas = null;
materialDatas = null;
bitmaps = null;
sequence = null;
if (loader != null) {
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loadNextBitmap);
loader.close();
loader = null;
}
}
private function on3DSLoad(e:Event):void {
data = urlLoader.data;
data.endian = Endian.LITTLE_ENDIAN;
parse3DSChunk(0, data.bytesAvailable);
urlLoader.removeEventListener(Event.COMPLETE, on3DSLoad);
urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, on3DSError);
urlLoader.removeEventListener(IOErrorEvent.NETWORK_ERROR, on3DSError);
urlLoader.removeEventListener(IOErrorEvent.VERIFY_ERROR, on3DSError);
urlLoader = null;
}
private function on3DSError(e:Event):void {
_content = null;
throw new IOError(IOErrorEvent(e).text);
urlLoader.removeEventListener(Event.COMPLETE, on3DSLoad);
urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, on3DSError);
urlLoader.removeEventListener(IOErrorEvent.NETWORK_ERROR, on3DSError);
urlLoader.removeEventListener(IOErrorEvent.VERIFY_ERROR, on3DSError);
}
private function loadBitmaps():void {
if (bitmaps != null) {
counter = 0;
sequence = new Array();
for (var filename:String in bitmaps) {
sequence.push(filename);
}
loader = new Loader();
loader.load(new URLRequest(path + sequence[counter]), context);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadNextBitmap);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadNextBitmap);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.NETWORK_ERROR, loadNextBitmap);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.VERIFY_ERROR, loadNextBitmap);
} else {
buildContent();
}
}
private function loadNextBitmap(e:Event = null):void {
if (!(e is IOErrorEvent)) {
bitmaps[sequence[counter]] = Bitmap(loader.content).bitmapData;
} else {
if (stub == null) {
var size:uint = 10;
stub = new BitmapData(size, size, false, 0);
for (var i:uint = 0; i < size; i++) {
for (var j:uint = 0; j < size; j+=2) {
stub.setPixel((i % 2) ? j : (j+1), i, 0xFF00FF);
}
}
}
bitmaps[sequence[counter]] = stub;
}
counter++;
if (counter < sequence.length) {
loader.load(new URLRequest(path + sequence[counter]), context);
} else {
buildContent();
}
if (e is IOErrorEvent) {
throw new IOError(IOErrorEvent(e).text);
}
}
private function buildContent():void {
var i:uint;
var length:uint;
// Формируем связи объектов
_content = new Object3D();
// Создаём материалы
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 = MathUtils.toRadian(mapData.rotation);
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 && objectData.vertices != null) {
// Меш
mesh = new Mesh(objectName);
animationData.object = mesh;
buildObject(animationData);
buildMesh(mesh, objectData, animationData);
} else {
var object:Object3D = new Object3D(objectName);
animationData.object = object;
buildObject(animationData);
}
}
buildHierarchy(_content, 0, length - 1);
}
} else {
for (objectName in objectDatas) {
objectData = objectDatas[objectName];
if (objectData.vertices != null) {
// Меш
mesh = new Mesh(objectName);
buildMesh(mesh, objectData, null);
_content.addChild(mesh);
}
}
}
// Рассылаем событие о завершении
dispatchEvent(new Event(Event.COMPLETE));
}
private function buildObject(animationData:AnimationData):void {
var object:Object3D = animationData.object;
if (animationData.position != null) {
object.x = animationData.position.x;
object.y = animationData.position.y;
object.z = animationData.position.z;
}
if (animationData.rotation != null) {
object.rotationX = animationData.rotation.x;
object.rotationY = animationData.rotation.y;
object.rotationZ = animationData.rotation.z;
}
if (animationData.scale != null) {
object.scaleX = animationData.scale.x;
object.scaleY = animationData.scale.y;
object.scaleZ = animationData.scale.z;
}
object.mobility = mobility;
}
private function buildMesh(mesh:Mesh, objectData:ObjectData, animationData:AnimationData):void {
// Добавляем вершины
var i:uint;
var length:uint = objectData.vertices.length;
for (i = 0; i < length; i++) {
var vertexData:Point3D = objectData.vertices[i];
objectData.vertices[i] = mesh.addVertex(vertexData.x, vertexData.y, vertexData.z, i);
}
// Коррекция вершин
if (animationData != null) {
// Инвертируем матрицу
objectData.matrix.invert();
// Вычитаем точку привязки из смещения матрицы
objectData.matrix.d -= animationData.pivot.x;
objectData.matrix.h -= animationData.pivot.y;
objectData.matrix.l -= animationData.pivot.z;
// Трансформируем вершины
for (var key:* in mesh._vertices) {
var vertex:Vertex = mesh._vertices[key];
vertex._coords.transform(objectData.matrix);
vertex._coords.multiply(units);
}
}
// Добавляем грани
length = objectData.faces.length;
for (i = 0; i < length; i++) {
var faceData:FaceData = objectData.faces[i];
mesh.addFace([objectData.vertices[faceData.a], objectData.vertices[faceData.b], objectData.vertices[faceData.c]], i);
}
// Добавляем поверхности
if (objectData.surfaces != null) {
for (var surfaceId:String in objectData.surfaces) {
var materialData:MaterialData = materialDatas[surfaceId];
var surfaceData:SurfaceData = objectData.surfaces[surfaceId];
var surface:Surface = mesh.addSurface(surfaceData.faces, surfaceId);
if (materialData.diffuseMap != null || materialData.normalMap != null) {
surface.material = new TextureMaterial(new Texture(bitmaps[materialData.diffuseMap.filename], materialData.diffuseMap.filename), 1 - materialData.transparency/100, repeat, smooth, blendMode, precision);
length = surfaceData.faces.length;
if (objectData.uvs != null) {
for (i = 0; i < length; i++) {
var id:uint = surfaceData.faces[i];
var fd:FaceData = objectData.faces[id];
var face:Face = mesh.getFaceById(id);
var aUV:Point = objectData.uvs[fd.a];
var bUV:Point = objectData.uvs[fd.b];
var cUV:Point = objectData.uvs[fd.c];
if (aUV != null && bUV != null && cUV != null && ((bUV.x - aUV.x)*(cUV.y - aUV.y) - (bUV.y - aUV.y)*(cUV.x - aUV.x) != 0)) {
face.aUV = materialData.matrix.transformPoint(aUV);
face.bUV = materialData.matrix.transformPoint(bUV);
face.cUV = materialData.matrix.transformPoint(cUV);
}
}
}
} else {
surface.material = new FillMaterial(materialDatas[surfaceId].color, 1 - materialData.transparency/100);
}
}
} else {
// Поверхность по умолчанию
var defaultSurface:Surface = mesh.addSurface();
// Добавляем грани
for (var faceId:String in mesh._faces) {
defaultSurface.addFace(mesh._faces[faceId]);
}
defaultSurface.material = new WireMaterial(0);
}
}
private function buildHierarchy(parent:Object3D, begin:uint, end:uint):void {
if (begin <= end) {
var animation:AnimationData = animationDatas[begin];
var object:Object3D = animation.object;
parent.addChild(object);
var parentIndex:uint = animation.parentIndex;
for (var i:uint = begin + 1; i <= end; i++) {
animation = animationDatas[i];
if (parentIndex == animation.parentIndex) {
buildHierarchy(object, begin + 1, i - 1);
buildHierarchy(parent, i, end);
return;
}
}
buildHierarchy(object, begin + 1, end);
}
}
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 = ColorUtils.rgb(data.readUnsignedByte(), data.readUnsignedByte(), 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.diffuseMap, dataIndex, dataLength);
break;
// Texture map 2
case 0xA33A:
break;
// Opacity map
case 0xA210:
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(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();
if (bitmaps == null) {
bitmaps = new Array();
}
bitmaps[map.filename] = null;
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(map, index + chunkLength, length - chunkLength);
}
}
private function parseObject(object:ObjectData, index:uint):uint {
// Создаём список объектов, если надо
if (objectDatas == null) {
objectDatas = new Array();
}
// Получаем название объекта
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 Array();
for (var i:uint = 0; i < num; i++) {
object.vertices.push(new Point3D(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 Array();
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();
object.matrix.a = data.readFloat();
object.matrix.e = data.readFloat();
object.matrix.i = data.readFloat();
object.matrix.b = data.readFloat();
object.matrix.f = data.readFloat();
object.matrix.j = data.readFloat();
object.matrix.c = data.readFloat();
object.matrix.g = data.readFloat();
object.matrix.k = data.readFloat();
object.matrix.d = data.readFloat();
object.matrix.h = data.readFloat();
object.matrix.l = data.readFloat();
}
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 Array();
}
// Получаем название материала
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 Point3D(data.readFloat(), data.readFloat(), data.readFloat());
}
private function parseObjectAnimationPosition(animation:AnimationData, index:uint):void {
data.position = index + 20;
animation.position = new Point3D(data.readFloat(), data.readFloat(), data.readFloat());
}
private function parseObjectAnimationRotation(animation:AnimationData, index:uint):void {
data.position = index + 20;
animation.rotation = getRotationFrom3DSAngleAxis(data.readFloat(), data.readFloat(), data.readFloat(), data.readFloat());
}
private function parseObjectAnimationScale(animation:AnimationData, index:uint):void {
data.position = index + 20;
animation.scale = new Point3D(data.readFloat(), data.readFloat(), data.readFloat());
}
public function get content():Object3D {
return _content;
}
// Считываем строку заканчивающуюся на нулевой байт
public 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):Point3D {
var res:Point3D = new Point3D();
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.core.Object3D;
import alternativa.types.Matrix3D;
import alternativa.types.Point3D;
import flash.geom.Matrix;
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 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:Array;
public var uvs:Array;
public var matrix:Matrix3D;
public var faces:Array;
public var surfaces:Array;
}
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:Object3D;
public var parentIndex:uint;
public var pivot:Point3D;
public var position:Point3D;
public var rotation:Point3D;
public var scale:Point3D;
}