Test commit

This commit is contained in:
maltsev
2012-03-28 17:37:50 +06:00
commit 93fff1889a
149 changed files with 37055 additions and 0 deletions

19
.gitignore vendored Normal file
View File

@@ -0,0 +1,19 @@
.idea/*
.idea/**/*
.settings/*
*.iml
*.svn*
*.DS_store*
# Build and Release Folders
bin-debug/
bin-release/
target/
out/
# Project property files
.actionScriptProperties
.flexProperties
.flexLibProperties
.settings/
.project

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
libs/OSGIBase.swc Normal file

Binary file not shown.

BIN
libs/ProtocolTypes.swc Normal file

Binary file not shown.

Binary file not shown.

56
pom-standalone.xml Normal file
View File

@@ -0,0 +1,56 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>platform.clients.fp10.libraries</groupId>
<artifactId>Alternativa3D</artifactId>
<packaging>swc</packaging>
<version>8.5.0.0-SNAPSHOT</version>
<parent>
<groupId>platform.clients.fp11.tools.maven</groupId>
<artifactId>BasePom</artifactId>
<version>2.11.1.0</version>
</parent>
<scm>
<connection>
scm:svn:https://svndev.alternativaplatform.com/platform/clients/fp11/libraries/Alternativa3D/trunk/
</connection>
</scm>
<dependencies>
<dependency>
<groupId>platform.client</groupId>
<artifactId>A3DModelsBase</artifactId>
<version>0.0.1.0</version>
<type>swc</type>
<scope>merged</scope>
</dependency>
<dependency>
<groupId>platform.clients.fp10</groupId>
<artifactId>OSGiBase</artifactId>
<version>2.0.2.0</version>
<type>swc</type>
<scope>merged</scope>
</dependency>
<!--<dependency>-->
<!--<groupId>platform.clients.fp10.libraries</groupId>-->
<!--<artifactId>AlternativaOSGi</artifactId>-->
<!--<version>2.0.14.0</version>-->
<!--<type>swc</type>-->
<!--<scope>merged</scope>-->
<!--</dependency>-->
<dependency>
<groupId>platform.clients.fp10.libraries</groupId>
<artifactId>AlternativaProtocol</artifactId>
<version>2.0.15.0</version>
<type>swc</type>
<scope>merged</scope>
</dependency>
<dependency>
<groupId>platform.clients.fp10.libraries</groupId>
<artifactId>ProtocolTypes</artifactId>
<version>1.0.1.0</version>
<type>swc</type>
<scope>merged</scope>
</dependency>
</dependencies>
</project>

57
pom.xml Normal file
View File

@@ -0,0 +1,57 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>platform.clients.fp11.libraries</groupId>
<artifactId>Alternativa3D</artifactId>
<packaging>swc</packaging>
<version>8.28.0-SNAPSHOT</version>
<parent>
<groupId>platform.clients.fp11.tools.maven</groupId>
<artifactId>BasePom</artifactId>
<version>2.58.0</version>
</parent>
<scm>
<connection>
scm:svn:https://svndev.alternativaplatform.com/platform/clients/fp11/libraries/Alternativa3D/trunk/
</connection>
</scm>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>platform.client.formats</groupId>
<artifactId>A3DModelsBase</artifactId>
<version>2.5.2</version>
<type>swc</type>
<scope>external</scope>
</dependency>
<dependency>
<groupId>platform.clients.fp10.libraries</groupId>
<artifactId>AlternativaProtocol</artifactId>
<version>2.53.0</version>
<type>swc</type>
<scope>external</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>platform.client.formats</groupId>
<artifactId>A3DModelsBase</artifactId>
<type>swc</type>
<scope>external</scope>
</dependency>
<dependency>
<groupId>platform.clients.fp10</groupId>
<artifactId>OSGiBase</artifactId>
<type>swc</type>
<scope>external</scope>
</dependency>
<dependency>
<groupId>platform.clients.fp10.libraries</groupId>
<artifactId>AlternativaProtocol</artifactId>
<type>swc</type>
<scope>external</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,24 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa {
/**
* Class contains information about version of a library.
* Also used for integration of a library to development tool of Adobe Flash.
*/
public class Alternativa3D {
/**
* Library version in the format: generation.feature-version.fix-version.
*/
public static const version:String = "8.27.0";
}
}

View File

@@ -0,0 +1,13 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d {
public namespace alternativa3d = "http://alternativaplatform.com/en/alternativa3d";
}

View File

@@ -0,0 +1,495 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.animation {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.animation.keys.Track;
import alternativa.engine3d.core.Object3D;
use namespace alternativa3d;
/**
*
* Plays complex animation which consists of a set of animation tracks.
* Every animated property of every model's element presented by separated track.
* Track is somewhat similar to separate animated layer in Flash, but the layer stores animation of all properties for some element at once.
* In opposite, track stores animation for every property (for an example, separate track for a scale and separate track for a coordinates).
* Track also can contain keyframes in arbitrary positions. Frames, contained between keyframes, are linearly interpolated
* (i.e. behave themselves like a timeline frames in flash, for which motion twin was created ).
* Animation clip connects each track with a specific object.
* Animation clip stores information about animation for the whole model, i.e. for any element state at any time moment.
* Animation works handled by <code>AnimationController</code>.
*
* @see alternativa.engine3d.animation.keys.Track
* @see alternativa.engine3d.animation.keys.TransformTrack
* @see alternativa.engine3d.animation.keys.NumberTrack
* @see alternativa.engine3d.animation.AnimationController
*/
public class AnimationClip extends AnimationNode {
/**
* @private
*/
alternativa3d var _objects:Array;
/**
* Name of the animation clip.
*/
public var name:String;
/**
* Defines if animation should be repeated.
*/
public var loop:Boolean = true;
/**
* Length of animation in seconds. If length of any animation track is changed, updateLength()
* method should be called to recalculate the length of the clip.
*
* @see #updateLength()
*/
public var length:Number = 0;
/**
* Handles the active animation execution. Plays animation if value is true.
*
* @see AnimationNode#isActive
*/
public var animated:Boolean = true;
/**
* @private
* Current value of time.
*/
private var _time:Number = 0;
/**
* @private
*/
private var _numTracks:int = 0;
/**
* @private
*/
private var _tracks:Vector.<Track> = new Vector.<Track>();
/**
* @private
*/
private var _notifiersList:AnimationNotify;
/**
* Creates a AnimationClip object.
*
* @param name name of the clip
*/
public function AnimationClip(name:String = null) {
this.name = name;
}
/**
* The list of animated objects. Animation tracks are bound to the objects by object names.
*
* @see Track#object
*/
public function get objects():Array {
return (_objects == null) ? null : [].concat(_objects);
}
/**
* @private
*/
public function set objects(value:Array):void {
updateObjects(_objects, controller, value, controller);
_objects = (value == null) ? null : [].concat(value);
}
/**
* @private
*/
override alternativa3d function setController(value:AnimationController):void {
updateObjects(_objects, controller, _objects, value);
this.controller = value;
}
/**
* @private
*/
private function addObject(object:Object):void {
if (_objects == null) {
_objects = [object];
} else {
_objects.push(object);
}
if (controller != null) {
controller.addObject(object);
}
}
/**
* @private
*/
private function updateObjects(oldObjects:Array, oldController:AnimationController, newObjects:Array, newController:AnimationController):void {
var i:int, count:int;
if (oldController != null && oldObjects != null) {
for (i = 0, count = _objects.length; i < count; i++) {
oldController.removeObject(oldObjects[i]);
}
}
if (newController != null && newObjects != null) {
for (i = 0, count = newObjects.length; i < count; i++) {
newController.addObject(newObjects[i]);
}
}
}
/**
* Updates the length of the clip in order to match with length of longest track.
* Should be called after track was changed.
*/
public function updateLength():void {
for (var i:int = 0; i < _numTracks; i++) {
var track:Track = _tracks[i];
var len:Number = track.length;
if (len > length) {
length = len;
}
}
}
/**
* Adds a new track to the animation clip.
* The total length of the clip is recalculated automatically.
*
* @param track track which should be added.
* @return added track.
*
* @see #length
*/
public function addTrack(track:Track):Track {
if (track == null) {
throw new Error("Track can not be null");
}
_tracks[_numTracks++] = track;
if (track.length > length) {
length = track.length;
}
return track;
}
/**
* Removes the specified track from the clip. The clip length is automatically recalculated.
*
* @param track track which should be removed.
* @return removed track.
*
* @see #length
* @throw Error if the AnimationClip does not include the track.
*/
public function removeTrack(track:Track):Track {
var index:int = _tracks.indexOf(track);
if (index < 0) throw new ArgumentError("Track not found");
_numTracks--;
var j:int = index + 1;
while (index < _numTracks) {
_tracks[index] = _tracks[j];
index++;
j++;
}
_tracks.length = _numTracks;
length = 0;
for (var i:int = 0; i < _numTracks; i++) {
var t:Track = _tracks[i];
if (t.length > length) {
length = t.length;
}
}
return track;
}
/**
* Returns the track object instance that exists at the specified index.
*
* @param index index.
* @return the track object instance that exists at the specified index.
*/
public function getTrackAt(index:int):Track {
return _tracks[index];
}
/**
* Number of tracks in the AnimationClip.
*/
public function get numTracks():int {
return _numTracks;
}
/**
* @private
*/
override alternativa3d function update(interval:Number, weight:Number):void {
var oldTime:Number = _time;
if (animated) {
_time += interval*speed;
if (loop) {
if (_time < 0) {
// _position = (length <= 0) ? 0 : _position % length;
_time = 0;
} else {
if (_time >= length) {
collectNotifiers(oldTime, length);
_time = (length <= 0) ? 0 : _time % length;
collectNotifiers(0, _time);
} else {
collectNotifiers(oldTime, _time);
}
}
} else {
if (_time < 0) {
_time = 0;
} else if (_time >= length) {
_time = length;
}
collectNotifiers(oldTime, _time);
}
}
if (weight > 0) {
for (var i:int = 0; i < _numTracks; i++) {
var track:Track = _tracks[i];
if (track.object != null) {
var state:AnimationState = controller.getState(track.object);
if (state != null) {
track.blend(_time, weight, state);
}
}
}
}
}
/**
* Current time of animation.
*/
public function get time():Number {
return _time;
}
/**
* @private
*/
public function set time(value:Number):void {
_time = value;
}
/**
* Current normalized time in the interval [0, 1].
*/
public function get normalizedTime():Number {
return (length == 0) ? 0 : _time/length;
}
/**
* @private
*/
public function set normalizedTime(value:Number):void {
_time = value*length;
}
/**
* @private
*/
private function getNumChildren(object:Object):int {
if (object is Object3D) {
return Object3D(object).numChildren;
}
return 0;
}
/**
* @private
*/
private function getChildAt(object:Object, index:int):Object {
if (object is Object3D) {
return Object3D(object).getChildAt(index);
}
return null;
}
/**
* @private
*/
private function addChildren(object:Object):void {
for (var i:int = 0, numChildren:int = getNumChildren(object); i < numChildren; i++) {
var child:Object = getChildAt(object, i);
addObject(child);
addChildren(child);
}
}
/**
* Binds tracks from the animation clip to given object. Only those tracks which have object property equal to the object's name are bound.
*
* @param object The object to which tracks are bound.
* @param includeDescendants If true, the whole tree of the object's children (if any) is processed.
*
* @see #objects
* @see alternativa.engine3d.animation.keys.Track#object
*/
public function attach(object:Object, includeDescendants:Boolean):void {
updateObjects(_objects, controller, null, controller);
_objects = null;
addObject(object);
if (includeDescendants) {
addChildren(object);
}
}
/**
* @private
*/
alternativa3d function collectNotifiers(start:Number, end:Number):void {
var notify:AnimationNotify = _notifiersList;
while (notify != null) {
if (notify._time > start) {
if (notify._time > end) {
notify = notify.next;
continue;
}
notify.processNext = controller.nearestNotifyers;
controller.nearestNotifyers = notify;
}
notify = notify.next;
}
}
/**
* Creates an AnimationNotify instance which is capable of firing notification events when playback reaches the specified time on the time line.
*
* @param time The time in seconds to which the notification trigger will be bound.
* @param name The name of AnimationNotify instance.
*
* @return A new instance of AnimationNotify class bound to specified time counting from start of the time line.
*
* @see AnimationNotify
*/
public function addNotify(time:Number, name:String = null):AnimationNotify {
time = (time <= 0) ? 0 : ((time >= length) ? length : time);
var notify:AnimationNotify = new AnimationNotify(name);
notify._time = time;
if (_notifiersList == null) {
_notifiersList = notify;
return notify;
} else {
if (_notifiersList._time > time) {
// Replaces the first key
notify.next = _notifiersList;
_notifiersList = notify;
return notify;
} else {
// Search for appropriate place
var n:AnimationNotify = _notifiersList;
while (n.next != null && n.next._time <= time) {
n = n.next;
}
if (n.next == null) {
// Places at the end
n.next = notify;
} else {
notify.next = n.next;
n.next = notify;
}
}
}
return notify;
}
/**
* Creates an AnimationNotify instance which is capable of firing notification events when playback reaches
* the specified time on the time line. The time is specified as an offset from the end of time line towards its start.
*
* @param offsetFromEnd The offset in seconds from the end of the time line towards its start, where the event object will be set in.
* @param name The name of notification trigger.
*
* @return A new instance of AnimationNotify class bound to specified time.
*
* @see AnimationNotify
*/
// TODO: name of method (addNotifyAtEnd) is incomprehensible. Rename to addNotifyFromFinish (or something else).
public function addNotifyAtEnd(offsetFromEnd:Number = 0, name:String = null):AnimationNotify {
return addNotify(length - offsetFromEnd, name);
}
/**
* Removes specified notification trigger.
*
* @param notify The notification trigger to remove.
* @return The removed notification trigger.
*/
public function removeNotify(notify:AnimationNotify):AnimationNotify {
if (_notifiersList != null) {
if (_notifiersList == notify) {
_notifiersList = _notifiersList.next;
return notify;
}
var n:AnimationNotify = _notifiersList;
while (n.next != null && n.next != notify) {
n = n.next;
}
if (n.next == notify) {
// removes
n.next = notify.next;
return notify;
}
}
throw new Error("Notify not found");
}
/**
* The list of notification triggers.
*/
public function get notifiers():Vector.<AnimationNotify> {
var result:Vector.<AnimationNotify> = new Vector.<AnimationNotify>();
var i:int = 0;
for (var notify:AnimationNotify = _notifiersList; notify != null; notify = notify.next) {
result[i] = notify;
i++;
}
return result;
}
/**
* Returns a fragment of the clip between specified bounds.
*
* @param start The start time of a fragment in seconds.
* @param end The end time of a fragment in seconds.
* @return The clip fragment.
*/
public function slice(start:Number, end:Number = Number.MAX_VALUE):AnimationClip {
var sliced:AnimationClip = new AnimationClip(name);
sliced._objects = (_objects == null) ? null : [].concat(_objects);
for (var i:int = 0; i < _numTracks; i++) {
sliced.addTrack(_tracks[i].slice(start, end));
}
return sliced;
}
/**
* Clones the clip. Both the clone and the original reference the same tracks.
*/
public function clone():AnimationClip {
var cloned:AnimationClip = new AnimationClip(name);
cloned._objects = (_objects == null) ? null : [].concat(_objects);
for (var i:int = 0; i < _numTracks; i++) {
cloned.addTrack(_tracks[i]);
}
cloned.length = length;
return cloned;
}
}
}

View File

@@ -0,0 +1,220 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.animation {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.animation.events.NotifyEvent;
import alternativa.engine3d.core.Object3D;
import flash.utils.Dictionary;
import flash.utils.getTimer;
use namespace alternativa3d;
/**
* Controls animation playback and blending. I.e. it animates model using information
* stored in <code>AnimationClip</code>-s and generated by <code>AnimationSwitcher</code>
* and <code>AnimationCouple</code> blenders.
* You have to call method <code>update()</code> each frame,
* which refreshes all child animation clips and blenders, which return
* list of properties and values to controller after that. You can use this list
* to set those properties. Controller sets those values and as a result
* the animation goes on. Animation control is carried out with the
* help of animated flag, and with <code>AnimationSwitcher</code> blender,
* which can transfer clip from active state to passive and vice versa.
*
*
* @see alternativa.engine3d.animation.AnimationClip
* @see alternativa.engine3d.animation.AnimationCouple
* @see alternativa.engine3d.animation.AnimationSwitcher
*/
public class AnimationController {
/**
* @private
*/
private var _root:AnimationNode;
/**
* @private
*/
private var _objects:Vector.<Object>;
/**
* @private
*/
private var _object3ds:Vector.<Object3D> = new Vector.<Object3D>();
/**
* @private
*/
private var objectsUsedCount:Dictionary = new Dictionary();
/**
* @private
*/
private var states:Object = new Object();
// private var datasList:BlendedData;
/**
* @private
*/
private var lastTime:int = -1;
/**
* @private
*/
alternativa3d var nearestNotifyers:AnimationNotify;
/**
* Creates a AnimationController object.
*/
public function AnimationController() {
}
/**
* Root of the animation tree.
*/
public function get root():AnimationNode {
return _root;
}
/**
* @private
*/
public function set root(value:AnimationNode):void {
if (_root != value) {
if (_root != null) {
_root.setController(null);
_root._isActive = false;
}
if (value != null) {
value.setController(this);
value._isActive = true;
}
this._root = value;
}
}
/**
* Plays animations on the time interval passed since the last <code>update()</code> call.
* If <code>freeze()</code> method was called after the last <code>update(),</code>
* animation will continue from that moment.
*/
public function update():void {
var interval:Number;
if (lastTime < 0) {
lastTime = getTimer();
interval = 0;
} else {
var time:int = getTimer();
interval = 0.001*(time - lastTime);
lastTime = time;
}
if (_root == null) {
return;
}
var data:AnimationState;
// Cleaning
for each (data in states) {
data.reset();
}
_root.update(interval, 1);
// Apply the animation
for (var i:int = 0, count:int = _object3ds.length; i < count; i++) {
var object:Object3D = _object3ds[i];
data = states[object.name];
if (data != null) {
data.apply(object);
}
}
// Calls the notifications
for (var notify:AnimationNotify = nearestNotifyers; notify != null; notify = notify.processNext) {
if (notify.willTrigger(NotifyEvent.NOTIFY)) {
notify.dispatchEvent(new NotifyEvent(notify));
}
}
nearestNotifyers = null;
}
/**
* @private
*/
alternativa3d function addObject(object:Object):void {
if (object in objectsUsedCount) {
objectsUsedCount[object]++;
} else {
if (object is Object3D) {
_object3ds.push(object);
} else {
_objects.push(object);
}
objectsUsedCount[object] = 1;
}
}
/**
* @private
*/
alternativa3d function removeObject(object:Object):void {
var used:int = objectsUsedCount[object];
used--;
if (used <= 0) {
var index:int;
var j:int;
var count:int;
if (object is Object3D) {
index = _object3ds.indexOf(object);
count = _object3ds.length - 1;
j = index + 1;
while (index < count) {
_object3ds[index] = _object3ds[j];
index++;
j++;
}
_object3ds.length = count;
} else {
index = _objects.indexOf(object);
count = _objects.length - 1;
j = index + 1;
while (index < count) {
_objects[index] = _objects[j];
index++;
j++;
}
_objects.length = count;
}
delete objectsUsedCount[object];
} else {
objectsUsedCount[object] = used;
}
}
/**
* @private
*/
alternativa3d function getState(name:String):AnimationState {
var state:AnimationState = states[name];
if (state == null) {
state = new AnimationState();
states[name] = state;
}
return state;
}
/**
* Freezes internal time counter till the next <code>update()<code> call.
*
* @see #update
*/
public function freeze():void {
lastTime = -1;
}
}
}

View File

@@ -0,0 +1,138 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.animation {
import alternativa.engine3d.alternativa3d;
use namespace alternativa3d;
/**
* Blends two animations according to the balance value.
* Mixes two animations with the given percentage. Any of <code>AnimationClip</code>,
* <code>AnimationSwitcher</code>, <code>AnimationCouple</code> classes can be blended.
*/
public class AnimationCouple extends AnimationNode {
/**
* @private
*/
private var _left:AnimationNode;
/**
* @private
*/
private var _right:AnimationNode;
/**
* The balance is a value in [0, 1] interval which specifies weight coefficient for each animation.
* The first (left) animation gets weight of (1 - balance) and the second (right) one gets weigth of balance.
*/
public var balance:Number = 0.5;
/**
* @private
*/
override alternativa3d function update(elapsed:Number, weight:Number):void {
var w:Number = (balance <= 0) ? 0 : ((balance >= 1) ? 1 : balance);
if (_left == null) {
_right.update(elapsed*speed, weight);
} else if (_right == null) {
_left.update(elapsed*speed, weight);
} else {
_left.update(elapsed*speed, (1 - w)*weight);
_right.update(elapsed*speed, w*weight);
}
}
/**
* @private
*/
override alternativa3d function setController(value:AnimationController):void {
this.controller = value;
if (_left != null) {
_left.setController(value);
}
if (_right != null) {
_right.setController(value);
}
}
/**
* @private
*/
override alternativa3d function addNode(node:AnimationNode):void {
super.addNode(node);
node._isActive = true;
}
/**
* @private
*/
override alternativa3d function removeNode(node:AnimationNode):void {
if (_left == node) {
_left = null;
} else {
_right = null;
}
super.removeNode(node);
}
/**
* The first animation.
*/
public function get left():AnimationNode {
return _left;
}
/**
* @private
*/
public function set left(value:AnimationNode):void {
if (value != _left) {
if (value._parent == this) {
throw new Error("Animation already exists in blender");
}
if (_left != null) {
removeNode(_left);
}
_left = value;
if (value != null) {
addNode(value);
}
}
}
/**
* The second animation.
*/
public function get right():AnimationNode {
return _right;
}
/**
* @private
*/
public function set right(value:AnimationNode):void {
if (value != _right) {
if (value._parent == this) {
throw new Error("Animation already exists in blender");
}
if (_right != null) {
removeNode(_right);
}
_right = value;
if (value != null) {
addNode(value);
}
}
}
}
}

View File

@@ -0,0 +1,96 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.animation {
import alternativa.engine3d.alternativa3d;
use namespace alternativa3d;
/**
* Animation tree node. Animation in Alternativa3D is built over the blend tree.
* This tree is intended for combining a set of animations and keeping unambiguous status
* of each property being animated in every frame. E.g. there can be independent animations
* for legs and hands, that will be presented by the nodes of blend tree. With the help of blenders,
* derived from <code>AnimationNode</code> you can change or blend nodes of blend tree. Every tree animation
* is controlled by <code>AnimationController</code>. <code>AnimationNode</code> instance have to be a root element of the tree.
*
*/
public class AnimationNode {
/**
* @private
*/
alternativa3d var _isActive:Boolean = false;
/**
* @private
*/
alternativa3d var _parent:AnimationNode;
/**
* @private
*/
alternativa3d var controller:AnimationController;
/**
* Animation speed.
*/
public var speed:Number = 1;
/**
* Determines if the animation is active.
*/
public function get isActive():Boolean {
return _isActive && controller != null;
}
/**
* Parent of this node in animation tree hierarchy.
*/
public function get parent():AnimationNode {
return _parent;
}
/**
* @private
*/
alternativa3d function update(elapsed:Number, weight:Number):void {
}
/**
* @private
*/
alternativa3d function setController(value:AnimationController):void {
this.controller = value;
}
/**
* @private
*/
alternativa3d function addNode(node:AnimationNode):void {
if (node._parent != null) {
node._parent.removeNode(node);
}
node._parent = this;
node.setController(controller);
}
/**
* @private
*/
alternativa3d function removeNode(node:AnimationNode):void {
node.setController(null);
node._isActive = false;
node._parent = null;
}
}
}

View File

@@ -0,0 +1,78 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.animation {
import alternativa.engine3d.alternativa3d;
import flash.events.EventDispatcher;
use namespace alternativa3d;
/**
* The notification trigger bound to certain time on an animation time line.
* <code>AnimationNotify</code> instance subscribes to <code>NotifyEvent.<NOTIFY /code>When animation
* playback reaches the given time, an event is dispatched by the trigger.
*
*<code>
* animationClip.addNotify(30).addEventListener(NotifyEvent.NOTIFY, notifyHandler)
* …
* private function notifyHandler(e:NotifyEvent):void{
* trace("Animation time is " + e.notify.time + " seconds now")
*}</code>
*
*
* @see AnimationClip#addNotify()
* @see AnimationClip#addNotifyAtEnd()
*/
public class AnimationNotify extends EventDispatcher {
/**
* The name of notification trigger.
*/
public var name:String;
/**
* @private
*/
alternativa3d var _time:Number = 0;
/**
* @private
*/
alternativa3d var next:AnimationNotify;
/**
* @private
*/
alternativa3d var updateTime:Number;
/**
* @private
*/
alternativa3d var processNext:AnimationNotify;
/**
* A new instance should not be created directly. Instead, use <code>AnimationClip.addNotify()</code> or <code>AnimationClip.addNotifyAtEnd()</code> methods.
*
* @see AnimationClip#addNotify()
* @see AnimationClip#addNotifyAtEnd()
*/
public function AnimationNotify(name:String) {
this.name = name;
}
/**
* The time in seconds on the time line to which the trigger is bound.
*/
public function get time():Number {
return _time;
}
}
}

View File

@@ -0,0 +1,262 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.animation {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.animation.keys.TransformKey;
import alternativa.engine3d.core.Object3D;
import flash.geom.Vector3D;
use namespace alternativa3d;
/**
* @private
*/
public class AnimationState {
public var useCount:int = 0;
public var transform:TransformKey = new TransformKey();
public var transformWeightSum:Number = 0;
public var numbers:Object = new Object();
public var numberWeightSums:Object = new Object();
public function AnimationState() {
}
public function reset():void {
transformWeightSum = 0;
for (var key:String in numbers) {
delete numbers[key];
delete numberWeightSums[key];
}
}
public function addWeightedTransform(key:TransformKey, weight:Number):void {
transformWeightSum += weight;
transform.interpolate(transform, key, weight/transformWeightSum);
}
public function addWeightedNumber(property:String, value:Number, weight:Number):void {
var sum:Number = numberWeightSums[property];
if (sum == sum) {
sum += weight;
weight /= sum;
var current:Number = numbers[property];
numbers[property] = (1 - weight)*current + weight*value;
numberWeightSums[property] = sum;
} else {
numbers[property] = value;
numberWeightSums[property] = weight;
}
}
public function apply(object:Object3D):void {
if (transformWeightSum > 0) {
object._x = transform.x;
object._y = transform.y;
object._z = transform.z;
setEulerAngles(transform.rotation, object);
object._scaleX = transform.scaleX;
object._scaleY = transform.scaleY;
object._scaleZ = transform.scaleZ;
object.transformChanged = true;
}
var sum:Number, weight:Number;
for (var key:String in numbers) {
switch (key) {
case 'x':
sum = numberWeightSums['x'];
weight = sum/(sum + transformWeightSum);
object.x = (1 - weight)*object.x + weight*numbers['x'];
break;
case 'y':
sum = numberWeightSums['y'];
weight = sum/(sum + transformWeightSum);
object.y = (1 - weight)*object.y + weight*numbers['y'];
break;
case 'z':
sum = numberWeightSums['z'];
weight = sum/(sum + transformWeightSum);
object.z = (1 - weight)*object.z + weight*numbers['z'];
break;
case 'rotationX':
sum = numberWeightSums['rotationX'];
weight = sum/(sum + transformWeightSum);
object.rotationX = (1 - weight)*object.rotationX + weight*numbers['rotationX'];
break;
case 'rotationY':
sum = numberWeightSums['rotationY'];
weight = sum/(sum + transformWeightSum);
object.rotationY = (1 - weight)*object.rotationY + weight*numbers['rotationY'];
break;
case 'rotationZ':
sum = numberWeightSums['rotationZ'];
weight = sum/(sum + transformWeightSum);
object.rotationZ = (1 - weight)*object.rotationZ + weight*numbers['rotationZ'];
break;
case 'scaleX':
sum = numberWeightSums['scaleX'];
weight = sum/(sum + transformWeightSum);
object.scaleX = (1 - weight)*object.scaleX + weight*numbers['scaleX'];
break;
case 'scaleY':
sum = numberWeightSums['scaleY'];
weight = sum/(sum + transformWeightSum);
object.scaleY = (1 - weight)*object.scaleY + weight*numbers['scaleY'];
break;
case 'scaleZ':
sum = numberWeightSums['scaleZ'];
weight = sum/(sum + transformWeightSum);
object.scaleZ = (1 - weight)*object.scaleZ + weight*numbers['scaleZ'];
break;
default :
object[key] = numbers[key];
break;
}
}
}
public function applyObject(object:Object):void {
if (transformWeightSum > 0) {
object.x = transform.x;
object.y = transform.y;
object.z = transform.z;
setEulerAnglesObject(transform.rotation, object);
object.scaleX = transform.scaleX;
object.scaleY = transform.scaleY;
object.scaleZ = transform.scaleZ;
}
var sum:Number, weight:Number;
for (var key:String in numbers) {
switch (key) {
case 'x':
sum = numberWeightSums['x'];
weight = sum/(sum + transformWeightSum);
object.x = (1 - weight)*object.x + weight*numbers['x'];
break;
case 'y':
sum = numberWeightSums['y'];
weight = sum/(sum + transformWeightSum);
object.y = (1 - weight)*object.y + weight*numbers['y'];
break;
case 'z':
sum = numberWeightSums['z'];
weight = sum/(sum + transformWeightSum);
object.z = (1 - weight)*object.z + weight*numbers['z'];
break;
case 'rotationX':
sum = numberWeightSums['rotationX'];
weight = sum/(sum + transformWeightSum);
object.rotationX = (1 - weight)*object.rotationX + weight*numbers['rotationX'];
break;
case 'rotationY':
sum = numberWeightSums['rotationY'];
weight = sum/(sum + transformWeightSum);
object.rotationY = (1 - weight)*object.rotationY + weight*numbers['rotationY'];
break;
case 'rotationZ':
sum = numberWeightSums['rotationZ'];
weight = sum/(sum + transformWeightSum);
object.rotationZ = (1 - weight)*object.rotationZ + weight*numbers['rotationZ'];
break;
case 'scaleX':
sum = numberWeightSums['scaleX'];
weight = sum/(sum + transformWeightSum);
object.scaleX = (1 - weight)*object.scaleX + weight*numbers['scaleX'];
break;
case 'scaleY':
sum = numberWeightSums['scaleY'];
weight = sum/(sum + transformWeightSum);
object.scaleY = (1 - weight)*object.scaleY + weight*numbers['scaleY'];
break;
case 'scaleZ':
sum = numberWeightSums['scaleZ'];
weight = sum/(sum + transformWeightSum);
object.scaleZ = (1 - weight)*object.scaleZ + weight*numbers['scaleZ'];
break;
default :
object[key] = numbers[key];
break;
}
}
}
private function setEulerAngles(quat:Vector3D, object:Object3D):void {
var qi2:Number = 2*quat.x*quat.x;
var qj2:Number = 2*quat.y*quat.y;
var qk2:Number = 2*quat.z*quat.z;
var qij:Number = 2*quat.x*quat.y;
var qjk:Number = 2*quat.y*quat.z;
var qki:Number = 2*quat.z*quat.x;
var qri:Number = 2*quat.w*quat.x;
var qrj:Number = 2*quat.w*quat.y;
var qrk:Number = 2*quat.w*quat.z;
var aa:Number = 1 - qj2 - qk2;
var bb:Number = qij - qrk;
var ee:Number = qij + qrk;
var ff:Number = 1 - qi2 - qk2;
var ii:Number = qki - qrj;
var jj:Number = qjk + qri;
var kk:Number = 1 - qi2 - qj2;
if (-1 < ii && ii < 1) {
object._rotationX = Math.atan2(jj, kk);
object._rotationY = -Math.asin(ii);
object._rotationZ = Math.atan2(ee, aa);
} else {
object._rotationX = 0;
object._rotationY = (ii <= -1) ? Math.PI : -Math.PI;
object._rotationY *= 0.5;
object._rotationZ = Math.atan2(-bb, ff);
}
}
private function setEulerAnglesObject(quat:Vector3D, object:Object):void {
var qi2:Number = 2*quat.x*quat.x;
var qj2:Number = 2*quat.y*quat.y;
var qk2:Number = 2*quat.z*quat.z;
var qij:Number = 2*quat.x*quat.y;
var qjk:Number = 2*quat.y*quat.z;
var qki:Number = 2*quat.z*quat.x;
var qri:Number = 2*quat.w*quat.x;
var qrj:Number = 2*quat.w*quat.y;
var qrk:Number = 2*quat.w*quat.z;
var aa:Number = 1 - qj2 - qk2;
var bb:Number = qij - qrk;
var ee:Number = qij + qrk;
var ff:Number = 1 - qi2 - qk2;
var ii:Number = qki - qrj;
var jj:Number = qjk + qri;
var kk:Number = 1 - qi2 - qj2;
if (-1 < ii && ii < 1) {
object.rotationX = Math.atan2(jj, kk);
object.rotationY = -Math.asin(ii);
object.rotationZ = Math.atan2(ee, aa);
} else {
object.rotationX = 0;
object.rotationY = (ii <= -1) ? Math.PI : -Math.PI;
object.rotationY *= 0.5;
object.rotationZ = Math.atan2(-bb, ff);
}
}
}
}

View File

@@ -0,0 +1,198 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.animation {
import alternativa.engine3d.alternativa3d;
use namespace alternativa3d;
/**
* The animation switcher performs animation blending and active animation switching.
*
*/
public class AnimationSwitcher extends AnimationNode {
/**
* @private
*/
private var _numAnimations:int = 0;
/**
* @private
*/
private var _animations:Vector.<AnimationNode> = new Vector.<AnimationNode>();
/**
* @private
*/
private var _weights:Vector.<Number> = new Vector.<Number>();
/**
* @private
*/
private var _active:AnimationNode;
/**
* @private
*/
private var fadingSpeed:Number = 0;
/**
* @private
*/
override alternativa3d function update(elapsed:Number, weight:Number):void {
// TODO : make fade if it required only
var interval:Number = speed * elapsed;
var fade:Number = fadingSpeed * interval;
for (var i:int = 0; i < _numAnimations; i++) {
var animation:AnimationNode = _animations[i];
var w:Number = _weights[i];
if (animation == _active) {
w += fade;
w = (w >= 1) ? 1 : w;
animation.update(interval, weight * w);
_weights[i] = w;
} else {
w -= fade;
if (w > 0) {
animation.update(interval, weight * w);
_weights[i] = w;
} else {
animation._isActive = false;
_weights[i] = 0;
}
}
}
}
/**
* The current active animation. To change active animation use <code>activate()</code>.
*
* @see #activate()
*/
public function get active():AnimationNode {
return _active;
}
/**
* Activates specified animation during given time interval. All the rest animations fade out.
*
* @param animation Animation which is set as active.
* @param time The time interval during which the animation becomes fully active (i.e. has full weight).
*/
public function activate(animation:AnimationNode, time:Number = 0):void {
if (animation._parent != this) {
throw new Error("Animation is not child of this blender");
}
_active = animation;
animation._isActive = true;
if (time <= 0) {
for (var i:int = 0; i < _numAnimations; i++) {
if (_animations[i] == animation) {
_weights[i] = 1;
} else {
_weights[i] = 0;
_animations[i]._isActive = false;
}
}
fadingSpeed = 0;
} else {
fadingSpeed = 1/time;
}
}
/**
* @private
*/
override alternativa3d function setController(value:AnimationController):void {
this.controller = value;
for (var i:int = 0; i < _numAnimations; i++) {
var animation:AnimationNode = _animations[i];
animation.setController(controller);
}
}
/**
* @private
*/
override alternativa3d function removeNode(node:AnimationNode):void {
removeAnimation(node);
}
/**
* Adds a new animation.
*
* @param animation The animation node to add.
* @return Added animation.
*/
public function addAnimation(animation:AnimationNode):AnimationNode {
if (animation == null) {
throw new Error("Animation cannot be null");
}
if (animation._parent == this) {
throw new Error("Animation already exist in blender");
}
_animations[_numAnimations] = animation;
if (_numAnimations == 0) {
_active = animation;
animation._isActive = true;
_weights[_numAnimations] = 1;
} else {
_weights[_numAnimations] = 0;
}
_numAnimations++;
addNode(animation);
return animation;
}
/**
* Removes child animation node.
*
* @param animation Animation node to remove.
*/
public function removeAnimation(animation:AnimationNode):AnimationNode {
var index:int = _animations.indexOf(animation);
if (index < 0) throw new ArgumentError("Animation not found");
_numAnimations--;
var j:int = index + 1;
while (index < _numAnimations) {
_animations[index] = _animations[j];
index++;
j++;
}
_animations.length = _numAnimations;
_weights.length = _numAnimations;
if (_active == animation) {
if (_numAnimations > 0) {
_active = _animations[int(_numAnimations - 1)];
_weights[int(_numAnimations - 1)] = 1;
} else {
_active = null;
}
}
super.removeNode(animation);
return animation;
}
/**
* Returns the child animation that exists at the specified index.
*
* @param index The index position of the child object.
*/
public function getAnimationAt(index:int):AnimationNode {
return _animations[index];
}
/**
* Returns number of animations.
*/
public function numAnimations():int {
return _numAnimations;
}
}
}

View File

@@ -0,0 +1,44 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.animation.events {
import alternativa.engine3d.animation.AnimationNotify;
import flash.events.Event;
/**
* This event is fired by an AnimationNotify instance when certain point of AnimationClip time line is reached.
*
* @see alternativa.engine3d.animation.AnimationNotify
*/
public class NotifyEvent extends Event {
/**
*NotifyEvent.NOTIFY is specified as the type property for transfer to the addEventListener alert for the event.
*/
public static const NOTIFY:String = "notify";
/**
* The source of the event. Actually this property returns AnimationNotify(target) value.
*/
public function get notify():AnimationNotify {
return AnimationNotify(target);
}
/**
* Creates a Notyfy object.
*/
public function NotifyEvent(notify:AnimationNotify) {
super(NOTIFY);
}
}
}

View File

@@ -0,0 +1,84 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.animation.keys {
import alternativa.engine3d.alternativa3d;
use namespace alternativa3d;
/**
* Keyframe of the animation. Sets object property at given time.
* Keyframe animation can be defined with NumberTrack and TransformTrack classes.
*
* @see TransformTrack
* @see NumberTrack
*/
public class Keyframe {
/**
* @private
* Key frame time in seconds.
*/
alternativa3d var _time:Number = 0;
/**
* Creates a new Keyframe instance.
*/
public function Keyframe() {
}
/**
* Key frame time in seconds.
*/
public function get time():Number {
return _time;
}
/**
* The value of animated property kept by the keyframe.
* Can be <code>Number</code> or <code>Matrix3D</code> depends on
* <code>NumberTrack</code> or <code>TransformTrack</code> belongs to.
*
* @see NumberTrack
* @see TransformTrack
*/
public function get value():Object {
return null;
}
/**
* @private
*/
public function set value(v:Object):void {
}
/**
* @private
*/
alternativa3d function get nextKeyFrame():Keyframe {
return null;
}
/**
* @private
*/
alternativa3d function set nextKeyFrame(value:Keyframe):void {
}
/**
* Returns string representation of the object.
*/
public function toString():String {
return '[Keyframe time = ' + _time.toFixed(2) + ' value = ' + value + ']';
}
}
}

View File

@@ -0,0 +1,73 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.animation.keys {
import alternativa.engine3d.alternativa3d;
use namespace alternativa3d;
/**
* @private
*/
public class NumberKey extends Keyframe {
/**
* @private
*/
alternativa3d var _value:Number = 0;
/**
* @private
*/
alternativa3d var next:NumberKey;
/**
* Creates a NumberKey object.
*/
public function NumberKey() {
}
/**
* Sets interpolated value.
*/
public function interpolate(a:NumberKey, b:NumberKey, c:Number):void {
_value = (1 - c)*a._value + c*b._value;
}
/**
* @inheritDoc
*/
override public function get value():Object {
return _value;
}
/**
* @inheritDoc
*/
override public function set value(v:Object):void {
_value = Number(v);
}
/**
* @inheritDoc
*/
override alternativa3d function get nextKeyFrame():Keyframe {
return next;
}
/**
* @inheritDoc
*/
override alternativa3d function set nextKeyFrame(value:Keyframe):void {
next = NumberKey(value);
}
}
}

View File

@@ -0,0 +1,160 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.animation.keys {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.animation.AnimationState;
use namespace alternativa3d;
/**
*
* Keyframe track for animating numeric properties. Each keyframe keeps its own value of the property.
* The value interpolates for in between keyframes.
*/
public class NumberTrack extends Track {
/**
* @private
* Head of keyframe list.
*/
alternativa3d var keyList:NumberKey;
private var lastKey:NumberKey;
/**
* @private
*/
override alternativa3d function get keyFramesList():Keyframe {
return keyList;
}
/**
* @private
*/
override alternativa3d function set keyFramesList(value:Keyframe):void {
keyList = NumberKey(value);
}
/**
* @private
*/
override alternativa3d function get lastKey():Keyframe {
return lastKey;
}
/**
* @private
*/
override alternativa3d function set lastKey(value:Keyframe):void {
lastKey = NumberKey(value);
}
/**
* Defines the name of object property which will be animated.
*/
public var property:String;
/**
* Creates a NumberTrack object.
*
* @param object name of animating object.
* @param property name of animating property.
*/
public function NumberTrack(object:String, property:String) {
this.property = property;
this.object = object;
}
/**
* Adds new keyframe. Keyframes stores ordered by its time property.
*
* @param time time of the new keyframe.
* @param value value of property for the new keyframe.
* @return added keyframe.
*/
public function addKey(time:Number, value:Number = 0):Keyframe {
var key:NumberKey = new NumberKey();
key._time = time;
key.value = value;
addKeyToList(key);
return key;
}
/**
* @private
*/
private static var temp:NumberKey = new NumberKey();
private var recentKey:NumberKey = null;
/**
* @private
*/
override alternativa3d function blend(time:Number, weight:Number, state:AnimationState):void {
if (property == null) {
return;
}
var prev:NumberKey;
var next:NumberKey;
if (recentKey != null && recentKey.time < time) {
prev = recentKey;
next = recentKey.next;
} else {
next = keyList;
}
while (next != null && next._time < time) {
prev = next;
next = next.next;
}
if (prev != null) {
if (next != null) {
temp.interpolate(prev, next, (time - prev._time)/(next._time - prev._time));
state.addWeightedNumber(property, temp._value, weight);
} else {
state.addWeightedNumber(property, prev._value, weight);
}
recentKey = prev;
} else {
if (next != null) {
state.addWeightedNumber(property, next._value, weight);
}
}
}
/**
* @private
*/
override alternativa3d function createKeyFrame():Keyframe {
return new NumberKey();
}
/**
* @private
*/
override alternativa3d function interpolateKeyFrame(dest:Keyframe, a:Keyframe, b:Keyframe, value:Number):void {
NumberKey(dest).interpolate(NumberKey(a), NumberKey(b), value);
}
/**
* @inheritDoc
*/
override public function slice(start:Number, end:Number = Number.MAX_VALUE):Track {
var track:NumberTrack = new NumberTrack(object, property);
sliceImplementation(track, start, end);
return track;
}
}
}

View File

@@ -0,0 +1,261 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.animation.keys {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.animation.AnimationState;
use namespace alternativa3d;
/**
* Keyframe track baseclass.
*
* @see alternativa.engine3d.animation.AnimationClip
*/
public class Track {
/**
* Name of the object which is animated.
*/
public var object:String;
/**
* @private
*/
alternativa3d var _length:Number = 0;
/**
* Creates a Track object.
*/
public function Track() {
}
/**
* The length of animation in seconds..
*/
public function get length():Number {
return _length;
}
/**
* @private
*/
alternativa3d function get keyFramesList():Keyframe {
return null;
}
/**
* @private
*/
alternativa3d function set keyFramesList(value:Keyframe):void {
}
/**
* @private
*/
alternativa3d function get lastKey():Keyframe {
return null;
}
/**
* @private
*/
alternativa3d function set lastKey(value:Keyframe):void {
}
/**
* @private
*/
alternativa3d function addKeyToList(key:Keyframe):void {
var time:Number = key._time;
if (keyFramesList == null) {
keyFramesList = key;
lastKey = key;
_length = (time <= 0) ? 0 : time;
return;
} else {
if (keyFramesList._time > time) {
// replace head of the keyframe list
key.nextKeyFrame = keyFramesList;
keyFramesList = key;
return;
} else {
// adds to the end of list
if (lastKey._time < time) {
lastKey.nextKeyFrame = key;
lastKey = key;
_length = (time <= 0) ? 0 : time;
} else {
// search for appropriate place
var k:Keyframe = keyFramesList;
while (k.nextKeyFrame != null && k.nextKeyFrame._time <= time) {
k = k.nextKeyFrame;
}
if (k.nextKeyFrame == null) {
// adds to the end
k.nextKeyFrame = key;
_length = (time <= 0) ? 0 : time;
} else {
key.nextKeyFrame = k.nextKeyFrame;
k.nextKeyFrame = key;
}
}
}
}
}
/**
* Removes the supplied key frame.
*
* @param key the key frame to remove.
* @return removed key frame.
*/
public function removeKey(key:Keyframe):Keyframe {
if (keyFramesList != null) {
if (keyFramesList == key) {
keyFramesList = keyFramesList.nextKeyFrame;
if (keyFramesList == null) {
lastKey = null;
_length = 0;
}
return key;
}
var k:Keyframe = keyFramesList;
while (k.nextKeyFrame != null && k.nextKeyFrame != key) {
k = k.nextKeyFrame;
}
if (k.nextKeyFrame == key) {
// Remove
if (key.nextKeyFrame == null) {
lastKey = k;
// Last item
_length = (k._time <= 0) ? 0 : k._time;
}
k.nextKeyFrame = key.nextKeyFrame;
return key;
}
}
throw new Error("Key not found");
}
/**
* Time-sorted list of key frames.
*/
public function get keys():Vector.<Keyframe> {
var result:Vector.<Keyframe> = new Vector.<Keyframe>();
var i:int = 0;
for (var key:Keyframe = keyFramesList; key != null; key = key.nextKeyFrame) {
result[i] = key;
i++;
}
return result;
}
/**
* @private
*/
alternativa3d function blend(time:Number, weight:Number, state:AnimationState):void {
}
/**
* Returns a fragment of animation track between start and end time.
*
* @param start Fragment's start time.
* @param end Fragment's end time.
* @return Track fragment.
*/
public function slice(start:Number, end:Number = Number.MAX_VALUE):Track {
return null;
}
/**
* @private
*/
alternativa3d function createKeyFrame():Keyframe {
return null;
}
/**
* @private
*/
alternativa3d function interpolateKeyFrame(dest:Keyframe, a:Keyframe, b:Keyframe, value:Number):void {
}
/**
* @private
*/
alternativa3d function sliceImplementation(dest:Track, start:Number, end:Number):void {
var shiftTime:Number = (start > 0) ? start : 0;
var prev:Keyframe;
var next:Keyframe = keyFramesList;
// the first keyframe
var key:Keyframe = createKeyFrame();
var nextKey:Keyframe;
while (next != null && next._time <= start) {
prev = next;
next = next.nextKeyFrame;
}
if (prev != null) {
if (next != null) {
interpolateKeyFrame(key, prev, next, (start - prev._time)/(next._time - prev._time));
key._time = start - shiftTime;
} else {
// last keyframe
interpolateKeyFrame(key, key, prev, 1);
}
} else {
if (next != null) {
// time before the start of animation
interpolateKeyFrame(key, key, next, 1);
key._time = next._time - shiftTime;
prev = next;
next = next.nextKeyFrame;
} else {
// empty track
return;
}
}
dest.keyFramesList = key;
if (next == null || end <= start) {
// one key frame
dest._length = (key._time <= 0) ? 0 : key._time;
return;
}
// copies intermediate keys
while (next != null && next._time <= end) {
nextKey = createKeyFrame();
interpolateKeyFrame(nextKey, nextKey, next, 1);
nextKey._time = next._time - shiftTime;
key.nextKeyFrame = nextKey;
key = nextKey;
prev = next;
next = next.nextKeyFrame;
}
// move last key
if (next != null) {
// time to end of the track
nextKey = createKeyFrame();
interpolateKeyFrame(nextKey, prev, next, (end - prev._time)/(next._time - prev._time));
nextKey._time = end - shiftTime;
key.nextKeyFrame = nextKey;
} else {
// time after current key
}
if (nextKey != null) {
dest._length = (nextKey._time <= 0) ? 0 : nextKey._time;
}
return;
}
}
}

View File

@@ -0,0 +1,164 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.animation.keys {
import alternativa.engine3d.alternativa3d;
import flash.geom.Matrix3D;
import flash.geom.Orientation3D;
import flash.geom.Vector3D;
use namespace alternativa3d;
/**
* @private
*/
public class TransformKey extends Keyframe {
/**
* @private
*/
alternativa3d var x:Number = 0;
/**
* @private
*/
alternativa3d var y:Number = 0;
/**
* @private
*/
alternativa3d var z:Number = 0;
/**
* @private
*/
alternativa3d var rotation:Vector3D = new Vector3D(0, 0, 0, 1);
/**
* @private
*/
alternativa3d var scaleX:Number = 1;
/**
* @private
*/
alternativa3d var scaleY:Number = 1;
/**
* @private
*/
alternativa3d var scaleZ:Number = 1;
/**
* @private
*/
alternativa3d var next:TransformKey;
/**
* Creates a TransformKey object.
*/
public function TransformKey() {
}
/**
* @inheritDoc
*/
override public function get value():Object {
var m:Matrix3D = new Matrix3D();
m.recompose(Vector.<Vector3D>([new Vector3D(x, y, z), rotation, new Vector3D(scaleX, scaleY, scaleZ)]), Orientation3D.QUATERNION);
return m;
}
/**
* @inheritDoc
*/
override public function set value(v:Object):void {
var m:Matrix3D = Matrix3D(v);
var components:Vector.<Vector3D> = m.decompose(Orientation3D.QUATERNION);
x = components[0].x;
y = components[0].y;
z = components[0].z;
rotation = components[1];
scaleX = components[2].x;
scaleY = components[2].y;
scaleZ = components[2].z;
}
/**
* Sets interpolated value.
*/
public function interpolate(a:TransformKey, b:TransformKey, c:Number):void {
var c2:Number = 1 - c;
x = c2*a.x + c*b.x;
y = c2*a.y + c*b.y;
z = c2*a.z + c*b.z;
slerp(a.rotation, b.rotation, c, rotation);
scaleX = c2*a.scaleX + c*b.scaleX;
scaleY = c2*a.scaleY + c*b.scaleY;
scaleZ = c2*a.scaleZ + c*b.scaleZ;
}
/**
* @private
*
* Performs spherical interpolation between two given quaternions by min distance
*
* @param a first quaternion.
* @param b second quaternion.
* @param t interpolation parameter, usually defines in [0, 1] range.
* @return this
*/
private function slerp(a:Vector3D, b:Vector3D, t:Number, result:Vector3D):void {
var flip:Number = 1;
// Since one orientation represents by two values q and -q, we should invert the sign of one of quaternions
// in case of negative value of the dot product. Otherwise the interpolation results by max distance.
var cosine:Number = a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z;
if (cosine < 0) {
cosine = -cosine;
flip = -1;
}
if ((1 - cosine) < 0.001) {
// Linear interpolation used near zero
var k1:Number = 1 - t;
var k2:Number = t*flip;
result.w = a.w*k1 + b.w*k2;
result.x = a.x*k1 + b.x*k2;
result.y = a.y*k1 + b.y*k2;
result.z = a.z*k1 + b.z*k2;
var d:Number = result.w*result.w + result.x*result.x + result.y*result.y + result.z*result.z;
if (d == 0) {
result.w = 1;
} else {
result.scaleBy(1/Math.sqrt(d));
}
} else {
var theta:Number = Math.acos(cosine);
var sine:Number = Math.sin(theta);
var beta:Number = Math.sin((1 - t)*theta)/sine;
var alpha:Number = Math.sin(t*theta)/sine*flip;
result.w = a.w*beta + b.w*alpha;
result.x = a.x*beta + b.x*alpha;
result.y = a.y*beta + b.y*alpha;
result.z = a.z*beta + b.z*alpha;
}
}
/**
* @inheritDoc
*/
override alternativa3d function get nextKeyFrame():Keyframe {
return next;
}
/**
* @inheritDoc
*/
override alternativa3d function set nextKeyFrame(value:Keyframe):void {
next = TransformKey(value);
}
}
}

View File

@@ -0,0 +1,240 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.animation.keys {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.animation.AnimationState;
import flash.geom.Matrix3D;
import flash.geom.Orientation3D;
import flash.geom.Vector3D;
use namespace alternativa3d;
/**
* A track which animates object transformation.
*/
public class TransformTrack extends Track {
private var keyList:TransformKey;
private var lastKey:TransformKey;
/**
* @private
*/
override alternativa3d function get keyFramesList():Keyframe {
return keyList;
}
/**
* @private
*/
override alternativa3d function set keyFramesList(value:Keyframe):void {
keyList = TransformKey(value);
}
/**
* @private
*/
override alternativa3d function get lastKey():Keyframe {
return lastKey;
}
/**
* @private
*/
override alternativa3d function set lastKey(value:Keyframe):void {
lastKey = TransformKey(value);
}
/**
* Creates a TransformTrack object.
*/
public function TransformTrack(object:String) {
this.object = object;
}
/**
* Adds new keyframe. Keyframes stores ordered by its time property.
*
* @param time time of the new keyframe.
* @param matrix value of property for the new keyframe.
* @return added keyframe.
*/
public function addKey(time:Number, matrix:Matrix3D):TransformKey {
var key:TransformKey = new TransformKey();
key._time = time;
var components:Vector.<Vector3D> = matrix.decompose(Orientation3D.QUATERNION);
key.x = components[0].x;
key.y = components[0].y;
key.z = components[0].z;
key.rotation = components[1];
key.scaleX = components[2].x;
key.scaleY = components[2].y;
key.scaleZ = components[2].z;
addKeyToList(key);
return key;
}
/**
* Adds new keyframe and initialize it by transformation components.
* Keyframes stores ordered by its time property.
*
* @param time time of the new keyframe.
* @return added keyframe.
*/
public function addKeyComponents(time:Number, x:Number = 0, y:Number = 0, z:Number = 0, rotationX:Number = 0, rotationY:Number = 0, rotationZ:Number = 0, scaleX:Number = 1, scaleY:Number = 1, scaleZ:Number = 1):TransformKey {
var key:TransformKey = new TransformKey();
key._time = time;
key.x = x;
key.y = y;
key.z = z;
key.rotation = createQuatFromEuler(rotationX, rotationY, rotationZ);
key.scaleX = scaleX;
key.scaleY = scaleY;
key.scaleZ = scaleZ;
addKeyToList(key);
return key;
}
/**
* @private
*
* Multiplies quat by additive from right: quat = quat * additive.
*
*/
private function appendQuat(quat:Vector3D, additive:Vector3D):void {
var ww:Number = additive.w*quat.w - additive.x*quat.x - additive.y*quat.y - additive.z*quat.z;
var xx:Number = additive.w*quat.x + additive.x*quat.w + additive.y*quat.z - additive.z*quat.y;
var yy:Number = additive.w*quat.y + additive.y*quat.w + additive.z*quat.x - additive.x*quat.z;
var zz:Number = additive.w*quat.z + additive.z*quat.w + additive.x*quat.y - additive.y*quat.x;
quat.w = ww;
quat.x = xx;
quat.y = yy;
quat.z = zz;
}
/**
* @private
*/
private function normalizeQuat(quat:Vector3D):void {
var d:Number = quat.w*quat.w + quat.x*quat.x + quat.y*quat.y + quat.z*quat.z;
if (d == 0) {
quat.w = 1;
} else {
d = 1/Math.sqrt(d);
quat.w *= d;
quat.x *= d;
quat.y *= d;
quat.z *= d;
}
}
/**
* @private
*/
private function setQuatFromAxisAngle(quat:Vector3D, x:Number, y:Number, z:Number, angle:Number):void {
quat.w = Math.cos(0.5*angle);
var k:Number = Math.sin(0.5*angle)/Math.sqrt(x*x + y*y + z*z);
quat.x = x*k;
quat.y = y*k;
quat.z = z*k;
}
/**
* @private
*/
private static var tempQuat:Vector3D = new Vector3D();
/**
* @private
*/
private function createQuatFromEuler(x:Number, y:Number, z:Number):Vector3D {
var result:Vector3D = new Vector3D();
setQuatFromAxisAngle(result, 1, 0, 0, x);
setQuatFromAxisAngle(tempQuat, 0, 1, 0, y);
appendQuat(result, tempQuat);
normalizeQuat(result);
setQuatFromAxisAngle(tempQuat, 0, 0, 1, z);
appendQuat(result, tempQuat);
normalizeQuat(result);
return result;
}
/**
* @private
*/
private static var temp:TransformKey = new TransformKey();
private var recentKey:TransformKey = null;
/**
* @private
*/
override alternativa3d function blend(time:Number, weight:Number, state:AnimationState):void {
var prev:TransformKey;
var next:TransformKey;
if (recentKey != null && recentKey.time < time) {
prev = recentKey;
next = recentKey.next;
} else {
next = keyList;
}
while (next != null && next._time < time) {
prev = next;
next = next.next;
}
if (prev != null) {
if (next != null) {
temp.interpolate(prev, next, (time - prev._time)/(next._time - prev._time));
state.addWeightedTransform(temp, weight);
} else {
state.addWeightedTransform(prev, weight);
}
recentKey = prev;
} else {
if (next != null) {
state.addWeightedTransform(next, weight);
}
}
}
/**
* @private
*/
override alternativa3d function createKeyFrame():Keyframe {
return new TransformKey();
}
/**
* @private
*/
override alternativa3d function interpolateKeyFrame(dest:Keyframe, a:Keyframe, b:Keyframe, value:Number):void {
TransformKey(dest).interpolate(TransformKey(a), TransformKey(b), value);
}
/**
* @inheritDoc
*/
override public function slice(start:Number, end:Number = Number.MAX_VALUE):Track {
var track:TransformTrack = new TransformTrack(object);
sliceImplementation(track, start, end);
return track;
}
}
}

View File

@@ -0,0 +1,576 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.collisions {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.*;
import alternativa.engine3d.resources.Geometry;
import flash.geom.Vector3D;
import flash.utils.ByteArray;
import flash.utils.Dictionary;
use namespace alternativa3d;
/**
* The class implements the algorithm of the continuous collision of an ellipsoid with the faces.
*/
public class EllipsoidCollider {
/**
* Ellipsoid radius along X axis.
*/
public var radiusX:Number;
/**
* Ellipsoid radius along Y axis.
*/
public var radiusY:Number;
/**
* Ellipsoid radius along Z axis.
*/
public var radiusZ:Number;
/**
* Geometric error. Minimum absolute difference between two values
* when they are considered to be different. Default value is 0.001.
*/
public var threshold:Number = 0.001;
private var matrix:Transform3D = new Transform3D();
private var inverseMatrix:Transform3D = new Transform3D();
/**
* @private
*/
alternativa3d var geometries:Vector.<Geometry> = new Vector.<Geometry>();
/**
* @private
*/
alternativa3d var transforms:Vector.<Transform3D> = new Vector.<Transform3D>();
private var vertices:Vector.<Number> = new Vector.<Number>();
private var normals:Vector.<Number> = new Vector.<Number>();
private var indices:Vector.<int> = new Vector.<int>();
private var numTriangles:int;
private var radius:Number;
private var src:Vector3D = new Vector3D();
private var displ:Vector3D = new Vector3D();
private var dest:Vector3D = new Vector3D();
private var collisionPoint:Vector3D = new Vector3D();
private var collisionPlane:Vector3D = new Vector3D();
/**
* @private
*/
alternativa3d var sphere:Vector3D = new Vector3D();
private var cornerA:Vector3D = new Vector3D();
private var cornerB:Vector3D = new Vector3D();
private var cornerC:Vector3D = new Vector3D();
private var cornerD:Vector3D = new Vector3D();
/**
* Creates a EllipsoidCollider object.
*
* @param radiusX Ellipsoid radius along X axis.
* @param radiusY Ellipsoid radius along Y axis.
* @param radiusZ Ellipsoid radius along Z axis.
*/
public function EllipsoidCollider(radiusX:Number, radiusY:Number, radiusZ:Number) {
this.radiusX = radiusX;
this.radiusY = radiusY;
this.radiusZ = radiusZ;
}
/**
* @private
*/
alternativa3d function calculateSphere(transform:Transform3D):void {
sphere.x = transform.d;
sphere.y = transform.h;
sphere.z = transform.l;
var sax:Number = transform.a*cornerA.x + transform.b*cornerA.y + transform.c*cornerA.z + transform.d;
var say:Number = transform.e*cornerA.x + transform.f*cornerA.y + transform.g*cornerA.z + transform.h;
var saz:Number = transform.i*cornerA.x + transform.j*cornerA.y + transform.k*cornerA.z + transform.l;
var sbx:Number = transform.a*cornerB.x + transform.b*cornerB.y + transform.c*cornerB.z + transform.d;
var sby:Number = transform.e*cornerB.x + transform.f*cornerB.y + transform.g*cornerB.z + transform.h;
var sbz:Number = transform.i*cornerB.x + transform.j*cornerB.y + transform.k*cornerB.z + transform.l;
var scx:Number = transform.a*cornerC.x + transform.b*cornerC.y + transform.c*cornerC.z + transform.d;
var scy:Number = transform.e*cornerC.x + transform.f*cornerC.y + transform.g*cornerC.z + transform.h;
var scz:Number = transform.i*cornerC.x + transform.j*cornerC.y + transform.k*cornerC.z + transform.l;
var sdx:Number = transform.a*cornerD.x + transform.b*cornerD.y + transform.c*cornerD.z + transform.d;
var sdy:Number = transform.e*cornerD.x + transform.f*cornerD.y + transform.g*cornerD.z + transform.h;
var sdz:Number = transform.i*cornerD.x + transform.j*cornerD.y + transform.k*cornerD.z + transform.l;
var dx:Number = sax - sphere.x;
var dy:Number = say - sphere.y;
var dz:Number = saz - sphere.z;
sphere.w = dx*dx + dy*dy + dz*dz;
dx = sbx - sphere.x;
dy = sby - sphere.y;
dz = sbz - sphere.z;
var dxyz:Number = dx*dx + dy*dy + dz*dz;
if (dxyz > sphere.w) sphere.w = dxyz;
dx = scx - sphere.x;
dy = scy - sphere.y;
dz = scz - sphere.z;
dxyz = dx*dx + dy*dy + dz*dz;
if (dxyz > sphere.w) sphere.w = dxyz;
dx = sdx - sphere.x;
dy = sdy - sphere.y;
dz = sdz - sphere.z;
dxyz = dx*dx + dy*dy + dz*dz;
if (dxyz > sphere.w) sphere.w = dxyz;
sphere.w = Math.sqrt(sphere.w);
}
private function prepare(source:Vector3D, displacement:Vector3D, object:Object3D, excludedObjects:Dictionary):void {
// Radius of the sphere
radius = radiusX;
if (radiusY > radius) radius = radiusY;
if (radiusZ > radius) radius = radiusZ;
// The matrix of the collider
matrix.compose(source.x, source.y, source.z, 0, 0, 0, radiusX/radius, radiusY/radius, radiusZ/radius);
inverseMatrix.copy(matrix);
inverseMatrix.invert();
// Local coordinates
src.x = 0;
src.y = 0;
src.z = 0;
// Local offset
displ.x = inverseMatrix.a*displacement.x + inverseMatrix.b*displacement.y + inverseMatrix.c*displacement.z;
displ.y = inverseMatrix.e*displacement.x + inverseMatrix.f*displacement.y + inverseMatrix.g*displacement.z;
displ.z = inverseMatrix.i*displacement.x + inverseMatrix.j*displacement.y + inverseMatrix.k*displacement.z;
// Local destination point
dest.x = src.x + displ.x;
dest.y = src.y + displ.y;
dest.z = src.z + displ.z;
// Bound defined by movement of the sphere
var rad:Number = radius + displ.length;
cornerA.x = -rad;
cornerA.y = -rad;
cornerA.z = -rad;
cornerB.x = rad;
cornerB.y = -rad;
cornerB.z = -rad;
cornerC.x = rad;
cornerC.y = rad;
cornerC.z = -rad;
cornerD.x = -rad;
cornerD.y = rad;
cornerD.z = -rad;
// Gathering the faces which with collision can occur
if (excludedObjects == null || !excludedObjects[object]) {
if (object.transformChanged) object.composeTransforms();
object.globalToLocalTransform.combine(object.inverseTransform, matrix);
// Check collision with the bound
var intersects:Boolean = true;
if (object.boundBox != null) {
calculateSphere(object.globalToLocalTransform);
intersects = object.boundBox.checkSphere(sphere);
}
if (intersects) {
object.localToGlobalTransform.combine(inverseMatrix, object.transform);
object.collectGeometry(this, excludedObjects);
}
// Check children
if (object.childrenList != null) object.collectChildrenGeometry(this, excludedObjects);
}
numTriangles = 0;
var indicesLength:int = 0;
var normalsLength:int = 0;
// Loop geometries
var j:int;
var mapOffset:int = 0;
var verticesLength:int = 0;
var geometriesLength:int = geometries.length;
for (var i:int = 0; i < geometriesLength; i++) {
var geometry:Geometry = geometries[i];
var transform:Transform3D = transforms[i];
var geometryIndicesLength:int = geometry._indices.length;
if (geometry._numVertices == 0 || geometryIndicesLength == 0) continue;
// Transform vertices
var vBuffer:VertexStream = (VertexAttributes.POSITION < geometry._attributesStreams.length) ? geometry._attributesStreams[VertexAttributes.POSITION] : null;
if (vBuffer != null) {
var attributesOffset:int = geometry._attributesOffsets[VertexAttributes.POSITION];
var numMappings:int = vBuffer.attributes.length;
var data:ByteArray = vBuffer.data;
for (j = 0; j < geometry._numVertices; j++) {
data.position = 4*(numMappings*j + attributesOffset);
var vx:Number = data.readFloat();
var vy:Number = data.readFloat();
var vz:Number = data.readFloat();
vertices[verticesLength] = transform.a*vx + transform.b*vy + transform.c*vz + transform.d; verticesLength++;
vertices[verticesLength] = transform.e*vx + transform.f*vy + transform.g*vz + transform.h; verticesLength++;
vertices[verticesLength] = transform.i*vx + transform.j*vy + transform.k*vz + transform.l; verticesLength++;
}
}
// Loop triangles
var geometryIndices:Vector.<uint> = geometry._indices;
for (j = 0; j < geometryIndicesLength;) {
var a:int = geometryIndices[j] + mapOffset; j++;
var index:int = a*3;
var ax:Number = vertices[index]; index++;
var ay:Number = vertices[index]; index++;
var az:Number = vertices[index];
var b:int = geometryIndices[j] + mapOffset; j++;
index = b*3;
var bx:Number = vertices[index]; index++;
var by:Number = vertices[index]; index++;
var bz:Number = vertices[index];
var c:int = geometryIndices[j] + mapOffset; j++;
index = c*3;
var cx:Number = vertices[index]; index++;
var cy:Number = vertices[index]; index++;
var cz:Number = vertices[index];
// Exclusion by bound
if (ax > rad && bx > rad && cx > rad || ax < -rad && bx < -rad && cx < -rad) continue;
if (ay > rad && by > rad && cy > rad || ay < -rad && by < -rad && cy < -rad) continue;
if (az > rad && bz > rad && cz > rad || az < -rad && bz < -rad && cz < -rad) continue;
// The normal
var abx:Number = bx - ax;
var aby:Number = by - ay;
var abz:Number = bz - az;
var acx:Number = cx - ax;
var acy:Number = cy - ay;
var acz:Number = cz - az;
var normalX:Number = acz*aby - acy*abz;
var normalY:Number = acx*abz - acz*abx;
var normalZ:Number = acy*abx - acx*aby;
var len:Number = normalX*normalX + normalY*normalY + normalZ*normalZ;
if (len < 0.001) continue;
len = 1/Math.sqrt(len);
normalX *= len;
normalY *= len;
normalZ *= len;
var offset:Number = ax*normalX + ay*normalY + az*normalZ;
if (offset > rad || offset < -rad) continue;
indices[indicesLength] = a; indicesLength++;
indices[indicesLength] = b; indicesLength++;
indices[indicesLength] = c; indicesLength++;
normals[normalsLength] = normalX; normalsLength++;
normals[normalsLength] = normalY; normalsLength++;
normals[normalsLength] = normalZ; normalsLength++;
normals[normalsLength] = offset; normalsLength++;
numTriangles++;
}
// Offset by nomber of vertices
mapOffset += geometry._numVertices;
}
geometries.length = 0;
transforms.length = 0;
}
/**
* Calculates destination point from given start position and displacement vector.
* @param source Starting point.
* @param displacement Displacement vector.
* @param object An object at crossing which will be checked. If this is a container, the application will participate and its child objects
* @param excludedObjects An associative array whose keys are instances of <code>Object3D</code> and its children.
* The objects that are keys of this dictionary will be excluded from intersection test.
* @return Destination point.
*/
public function calculateDestination(source:Vector3D, displacement:Vector3D, object:Object3D, excludedObjects:Dictionary = null):Vector3D {
if (displacement.length <= threshold) return source.clone();
prepare(source, displacement, object, excludedObjects);
if (numTriangles > 0) {
var limit:int = 50;
for (var i:int = 0; i < limit; i++) {
if (checkCollision()) {
// Offset destination point from behind collision plane by radius of the sphere over plane, along the normal
var offset:Number = radius + threshold + collisionPlane.w - dest.x*collisionPlane.x - dest.y*collisionPlane.y - dest.z*collisionPlane.z;
dest.x += collisionPlane.x*offset;
dest.y += collisionPlane.y*offset;
dest.z += collisionPlane.z*offset;
// Fixing up the current sphere coordinates for the next iteration
src.x = collisionPoint.x + collisionPlane.x*(radius + threshold);
src.y = collisionPoint.y + collisionPlane.y*(radius + threshold);
src.z = collisionPoint.z + collisionPlane.z*(radius + threshold);
// Fixing up velocity vector. The result ordered along plane of collision.
displ.x = dest.x - src.x;
displ.y = dest.y - src.y;
displ.z = dest.z - src.z;
if (displ.length < threshold) break;
} else break;
}
// Setting the coordinates
return new Vector3D(matrix.a*dest.x + matrix.b*dest.y + matrix.c*dest.z + matrix.d, matrix.e*dest.x + matrix.f*dest.y + matrix.g*dest.z + matrix.h, matrix.i*dest.x + matrix.j*dest.y + matrix.k*dest.z + matrix.l);
} else {
return new Vector3D(source.x + displacement.x, source.y + displacement.y, source.z + displacement.z);
}
}
/**
* Finds first collision from given starting point aling displacement vector.
* @param source Starting point.
* @param displacement Displacement vector.
* @param resCollisionPoint Collision point will be written into this variable.
* @param resCollisionPlane Collision plane (defines by normal) parameters will be written into this variable.
* @param object The object to use in collision detection. If a container is specified, all its children will be tested for collison with ellipsoid.
* @param excludedObjects An associative array whose keys are instances of <code>Object3D</code> and its children.
* @return <code>true</code> if collision detected and <code>false</code> otherwise.
*/
public function getCollision(source:Vector3D, displacement:Vector3D, resCollisionPoint:Vector3D, resCollisionPlane:Vector3D, object:Object3D, excludedObjects:Dictionary = null):Boolean {
if (displacement.length <= threshold) return false;
prepare(source, displacement, object, excludedObjects);
if (numTriangles > 0) {
if (checkCollision()) {
// Transform the point to the global space
resCollisionPoint.x = matrix.a*collisionPoint.x + matrix.b*collisionPoint.y + matrix.c*collisionPoint.z + matrix.d;
resCollisionPoint.y = matrix.e*collisionPoint.x + matrix.f*collisionPoint.y + matrix.g*collisionPoint.z + matrix.h;
resCollisionPoint.z = matrix.i*collisionPoint.x + matrix.j*collisionPoint.y + matrix.k*collisionPoint.z + matrix.l;
// Transform the plane to the global space
var abx:Number;
var aby:Number;
var abz:Number;
if (collisionPlane.x < collisionPlane.y) {
if (collisionPlane.x < collisionPlane.z) {
abx = 0;
aby = -collisionPlane.z;
abz = collisionPlane.y;
} else {
abx = -collisionPlane.y;
aby = collisionPlane.x;
abz = 0;
}
} else {
if (collisionPlane.y < collisionPlane.z) {
abx = collisionPlane.z;
aby = 0;
abz = -collisionPlane.x;
} else {
abx = -collisionPlane.y;
aby = collisionPlane.x;
abz = 0;
}
}
var acx:Number = collisionPlane.z*aby - collisionPlane.y*abz;
var acy:Number = collisionPlane.x*abz - collisionPlane.z*abx;
var acz:Number = collisionPlane.y*abx - collisionPlane.x*aby;
var abx2:Number = matrix.a*abx + matrix.b*aby + matrix.c*abz;
var aby2:Number = matrix.e*abx + matrix.f*aby + matrix.g*abz;
var abz2:Number = matrix.i*abx + matrix.j*aby + matrix.k*abz;
var acx2:Number = matrix.a*acx + matrix.b*acy + matrix.c*acz;
var acy2:Number = matrix.e*acx + matrix.f*acy + matrix.g*acz;
var acz2:Number = matrix.i*acx + matrix.j*acy + matrix.k*acz;
resCollisionPlane.x = abz2*acy2 - aby2*acz2;
resCollisionPlane.y = abx2*acz2 - abz2*acx2;
resCollisionPlane.z = aby2*acx2 - abx2*acy2;
resCollisionPlane.normalize();
resCollisionPlane.w = resCollisionPoint.x*resCollisionPlane.x + resCollisionPoint.y*resCollisionPlane.y + resCollisionPoint.z*resCollisionPlane.z;
return true;
} else {
return false;
}
}
return false;
}
private function checkCollision():Boolean {
var minTime:Number = 1;
var displacementLength:Number = displ.length;
// Loop triangles
var indicesLength:int = numTriangles*3;
for (var i:int = 0, j:int = 0; i < indicesLength;) {
// Points
var index:int = indices[i]*3; i++;
var ax:Number = vertices[index]; index++;
var ay:Number = vertices[index]; index++;
var az:Number = vertices[index];
index = indices[i]*3; i++;
var bx:Number = vertices[index]; index++;
var by:Number = vertices[index]; index++;
var bz:Number = vertices[index];
index = indices[i]*3; i++;
var cx:Number = vertices[index]; index++;
var cy:Number = vertices[index]; index++;
var cz:Number = vertices[index];
// Normal
var normalX:Number = normals[j]; j++;
var normalY:Number = normals[j]; j++;
var normalZ:Number = normals[j]; j++;
var offset:Number = normals[j]; j++;
var distance:Number = src.x*normalX + src.y*normalY + src.z*normalZ - offset;
// The intersection of plane and sphere
var pointX:Number;
var pointY:Number;
var pointZ:Number;
if (distance < radius) {
pointX = src.x - normalX*distance;
pointY = src.y - normalY*distance;
pointZ = src.z - normalZ*distance;
} else {
var t:Number = (distance - radius)/(distance - dest.x*normalX - dest.y*normalY - dest.z*normalZ + offset);
pointX = src.x + displ.x*t - normalX*radius;
pointY = src.y + displ.y*t - normalY*radius;
pointZ = src.z + displ.z*t - normalZ*radius;
}
// Closest polygon vertex
var faceX:Number;
var faceY:Number;
var faceZ:Number;
var min:Number = 1e+22;
// Loop edges
var inside:Boolean = true;
for (var k:int = 0; k < 3; k++) {
var p1x:Number;
var p1y:Number;
var p1z:Number;
var p2x:Number;
var p2y:Number;
var p2z:Number;
if (k == 0) {
p1x = ax;
p1y = ay;
p1z = az;
p2x = bx;
p2y = by;
p2z = bz;
} else if (k == 1) {
p1x = bx;
p1y = by;
p1z = bz;
p2x = cx;
p2y = cy;
p2z = cz;
} else {
p1x = cx;
p1y = cy;
p1z = cz;
p2x = ax;
p2y = ay;
p2z = az;
}
var abx:Number = p2x - p1x;
var aby:Number = p2y - p1y;
var abz:Number = p2z - p1z;
var acx:Number = pointX - p1x;
var acy:Number = pointY - p1y;
var acz:Number = pointZ - p1z;
var crx:Number = acz*aby - acy*abz;
var cry:Number = acx*abz - acz*abx;
var crz:Number = acy*abx - acx*aby;
// Case of the point is outside of the polygon
if (crx*normalX + cry*normalY + crz*normalZ < 0) {
var edgeLength:Number = abx*abx + aby*aby + abz*abz;
var edgeDistanceSqr:Number = (crx*crx + cry*cry + crz*crz)/edgeLength;
if (edgeDistanceSqr < min) {
// Edge normalization
edgeLength = Math.sqrt(edgeLength);
abx /= edgeLength;
aby /= edgeLength;
abz /= edgeLength;
// Distance to intersecion of normal along theedge
t = abx*acx + aby*acy + abz*acz;
var acLen:Number;
if (t < 0) {
// Closest point is the first one
acLen = acx*acx + acy*acy + acz*acz;
if (acLen < min) {
min = acLen;
faceX = p1x;
faceY = p1y;
faceZ = p1z;
}
} else if (t > edgeLength) {
// Closest point is the second one
acx = pointX - p2x;
acy = pointY - p2y;
acz = pointZ - p2z;
acLen = acx*acx + acy*acy + acz*acz;
if (acLen < min) {
min = acLen;
faceX = p2x;
faceY = p2y;
faceZ = p2z;
}
} else {
// Closest point is on edge
min = edgeDistanceSqr;
faceX = p1x + abx*t;
faceY = p1y + aby*t;
faceZ = p1z + abz*t;
}
}
inside = false;
}
}
// Case of point is inside polygon
if (inside) {
faceX = pointX;
faceY = pointY;
faceZ = pointZ;
}
// Vector pointed from closest point to the center of sphere
var deltaX:Number = src.x - faceX;
var deltaY:Number = src.y - faceY;
var deltaZ:Number = src.z - faceZ;
// If movement directed to point
if (deltaX*displ.x + deltaY*displ.y + deltaZ*displ.z <= 0) {
// reversed vector
var backX:Number = -displ.x/displacementLength;
var backY:Number = -displ.y/displacementLength;
var backZ:Number = -displ.z/displacementLength;
// Length of Vector pointed from closest point to the center of sphere
var deltaLength:Number = deltaX*deltaX + deltaY*deltaY + deltaZ*deltaZ;
// Projection Vector pointed from closest point to the center of sphere on reversed vector
var projectionLength:Number = deltaX*backX + deltaY*backY + deltaZ*backZ;
var projectionInsideLength:Number = radius*radius - deltaLength + projectionLength*projectionLength;
if (projectionInsideLength > 0) {
// Time of the intersection
var time:Number = (projectionLength - Math.sqrt(projectionInsideLength))/displacementLength;
// Collision with closest point occurs
if (time < minTime) {
minTime = time;
collisionPoint.x = faceX;
collisionPoint.y = faceY;
collisionPoint.z = faceZ;
if (inside) {
collisionPlane.x = normalX;
collisionPlane.y = normalY;
collisionPlane.z = normalZ;
collisionPlane.w = offset;
} else {
deltaLength = Math.sqrt(deltaLength);
collisionPlane.x = deltaX/deltaLength;
collisionPlane.y = deltaY/deltaLength;
collisionPlane.z = deltaZ/deltaLength;
collisionPlane.w = collisionPoint.x*collisionPlane.x + collisionPoint.y*collisionPlane.y + collisionPoint.z*collisionPlane.z;
}
}
}
}
}
return minTime < 1;
}
}
}

View File

@@ -0,0 +1,486 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
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.Matrix3D;
import flash.geom.Point;
import flash.geom.Vector3D;
import flash.ui.Keyboard;
import flash.utils.getTimer;
/**
* Controller for <code>Object3D</code>. Allow to handle the object with a keyboard and mouse.
*
* @see alternativa.engine3d.core.Object3D
*/
public class SimpleObjectController {
/**
* Name of action for binding "forward" action.
*/
public static const ACTION_FORWARD:String = "ACTION_FORWARD";
/**
* Name of action for binding "back" action.
*/
public static const ACTION_BACK:String = "ACTION_BACK";
/**
* Name of action for binding "left" action.
*/
public static const ACTION_LEFT:String = "ACTION_LEFT";
/**
* Name of action for binding "right" action.
*/
public static const ACTION_RIGHT:String = "ACTION_RIGHT";
/**
* Name of action for binding "up" action.
*/
public static const ACTION_UP:String = "ACTION_UP";
/**
* Name of action for binding "down" action.
*/
public static const ACTION_DOWN:String = "ACTION_DOWN";
/**
* Name of action for binding "pitch up" action.
*/
public static const ACTION_PITCH_UP:String = "ACTION_PITCH_UP";
/**
* Name of action for binding "pitch down" action.
*/
public static const ACTION_PITCH_DOWN:String = "ACTION_PITCH_DOWN";
/**
* Name of action for binding "yaw left" action.
*/
public static const ACTION_YAW_LEFT:String = "ACTION_YAW_LEFT";
/**
* Name of action for binding "yaw right" action.
*/
public static const ACTION_YAW_RIGHT:String = "ACTION_YAW_RIGHT";
/**
* Name of action for binding "accelerate" action.
*/
public static const ACTION_ACCELERATE:String = "ACTION_ACCELERATE";
/**
* ИName of action for binding "mouse look" action.
*/
public static const ACTION_MOUSE_LOOK:String = "ACTION_MOUSE_LOOK";
/**
* Speed.
*/
public var speed:Number;
/**
* Speed multiplier for acceleration mode.
*/
public var speedMultiplier:Number;
/**
* Mouse sensitivity.
*/
public var mouseSensitivity:Number;
/**
* The maximal slope in the vertical plane in radians.
*/
public var maxPitch:Number = 1e+22;
/**
* The minimal slope in the vertical plane in radians.
*/
public var minPitch:Number = -1e+22;
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;
/**
* The hash for binding names of action and functions. The functions should be at a form are follows:
* <code>
* function(value:Boolean):void
* </code>
*
* <code>value</code> argument defines if bound key pressed down or up.
*/
private var actionBindings:Object = {};
/**
* The hash for binding key codes and action names.
*/
protected var keyBindings:Object = {};
/**
* Creates a SimpleObjectController object.
* @param eventSource Source for event listening.
* @param speed Speed of movement.
* @param mouseSensitivity Mouse sensitivity, i.e. number of degrees per each pixel of mouse movement.
*/
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();
}
/**
* Enables the controler.
*/
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);
}
/**
* Disables the controller.
*/
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();
}
/**
* Enables mouse look mode.
*/
public function startMouseLook():void {
mousePoint.x = eventSource.mouseX;
mousePoint.y = eventSource.mouseY;
mouseLook = true;
}
/**
* Disables mouse look mode.
*/
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);
}
/**
* Target of handling.
*/
public function get object():Object3D {
return _object;
}
/**
* @private
*/
public function set object(value:Object3D):void {
_object = value;
updateObjectTransform();
}
/**
* Refreshes controller state from state of handled object. Should be called if object was moved without the controller (i.e. <code>object.x = 100;</code>).
*/
public function updateObjectTransform():void {
if (_object != null) objectTransform = _object.matrix.decompose();
}
/**
* Calculates and sets new object position.
*/
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) {
var m:Matrix3D = new Matrix3D();
m.recompose(objectTransform);
_object.matrix = m;
}
}
/**
* Sets object at given position.
* @param pos The position.
*/
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;
}
}
/**
* Sets object at given position.
* @param x X.
* @param y Y.
* @param z Z.
*/
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;
}
}
/**
* Sets direction of Z-axis of handled object to pointed at given place. If object is a camera, it will look to this direction.
* @param point Point to look at.
*/
public function lookAt(point:Vector3D):void {
lookAtXYZ(point.x, point.y, point.z);
}
/**
* Sets direction of Z-axis of handled object to pointed at given place. If object is a camera, it will look to this direction.
* @param x X.
* @param y Y.
* @param z 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);
var m:Matrix3D = _object.matrix;
m.recompose(objectTransform);
_object.matrix = m;
}
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;
}
/**
* Starts and stops move forward according to <code>true</code> or <code>false</code> was passed.
* @param value Action switcher.
*/
public function moveForward(value:Boolean):void {
_forward = value;
}
/**
* Starts and stops move backward according to <code>true</code> or <code>false</code> was passed.
* @param value Action switcher.
*/
public function moveBack(value:Boolean):void {
_back = value;
}
/**
* Starts and stops move to left according to <code>true</code> or <code>false</code> was passed.
* @param value Action switcher.
*/
public function moveLeft(value:Boolean):void {
_left = value;
}
/**
* Starts and stops move to right according to <code>true</code> or <code>false</code> was passed.
* @param value Action switcher.
*/
public function moveRight(value:Boolean):void {
_right = value;
}
/**
* Starts and stops move up according to <code>true</code> or <code>false</code> was passed.
* @param value Action switcher.
*/
public function moveUp(value:Boolean):void {
_up = value;
}
/**
* Starts and stops move down according to <code>true</code> or <code>false</code> was passed.
* @param value Action switcher.
*/
public function moveDown(value:Boolean):void {
_down = value;
}
/**
* Switches acceleration mode.
* @param value <code>true</code> turns acceleration on, <code>false</code> turns off.
*/
public function accelerate(value:Boolean):void {
_accelerate = value;
}
/**
* Binds key and action. Only one action can be assigned to one key.
* @param keyCode Key code.
* @param action Action name.
* @see #unbindKey()
* @see #unbindAll()
*/
public function bindKey(keyCode:uint, action:String):void {
var method:Function = actionBindings[action];
if (method != null) keyBindings[keyCode] = method;
}
/**
* Binds keys and actions. Only one action can be assigned to one key.
* @param bindings Array which consists of sequence of couples of key code and action. An example are follows: <code> [ keyCode1, action1, keyCode2, action2 ] </code>.
*/
public function bindKeys(bindings:Array):void {
for (var i:int = 0; i < bindings.length; i += 2) bindKey(bindings[i], bindings[i + 1]);
}
/**
* Clear binding for given keyCode.
* @param keyCode Key code.
* @see #bindKey()
* @see #unbindAll()
*/
public function unbindKey(keyCode:uint):void {
delete keyBindings[keyCode];
}
/**
* Clear binding of all keys.
* @see #bindKey()
* @see #unbindKey()
*/
public function unbindAll():void {
for (var key:String in keyBindings) delete keyBindings[key];
}
/**
* Sets default binding.
* @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);
}
}
}

View File

@@ -0,0 +1,299 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.core {
import alternativa.engine3d.alternativa3d;
import flash.geom.Vector3D;
use namespace alternativa3d;
/**
* Class stores object's bounding box object's local space. Generally, position of child objects isn't considered at BoundBox calculation.
* Ray intersection always made boundBox check at first, but it's possible to check on crossing boundBox only.
*
*/
public class BoundBox {
/**
* Left face.
*/
public var minX:Number = 1e+22;
/**
* Back face.
*/
public var minY:Number = 1e+22;
/**
* Bottom face.
*/
public var minZ:Number = 1e+22;
/**
* Right face.
*/
public var maxX:Number = -1e+22;
/**
* Ftont face.
*/
public var maxY:Number = -1e+22;
/**
* Top face.
*/
public var maxZ:Number = -1e+22;
/**
* Resets all bounds values to its initial state.
*/
public function reset():void {
minX = 1e+22;
minY = 1e+22;
minZ = 1e+22;
maxX = -1e+22;
maxY = -1e+22;
maxZ = -1e+22;
}
/**
* @private
*/
alternativa3d function checkFrustumCulling(frustum:CullingPlane, culling:int):int {
var side:int = 1;
for (var plane:CullingPlane = frustum; plane != null; plane = plane.next) {
if (culling & side) {
if (plane.x >= 0) if (plane.y >= 0) if (plane.z >= 0) {
if (maxX*plane.x + maxY*plane.y + maxZ*plane.z <= plane.offset) return -1;
if (minX*plane.x + minY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
} else {
if (maxX*plane.x + maxY*plane.y + minZ*plane.z <= plane.offset) return -1;
if (minX*plane.x + minY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side);
} else if (plane.z >= 0) {
if (maxX*plane.x + minY*plane.y + maxZ*plane.z <= plane.offset) return -1;
if (minX*plane.x + maxY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
} else {
if (maxX*plane.x + minY*plane.y + minZ*plane.z <= plane.offset) return -1;
if (minX*plane.x + maxY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side);
} else if (plane.y >= 0) if (plane.z >= 0) {
if (minX*plane.x + maxY*plane.y + maxZ*plane.z <= plane.offset) return -1;
if (maxX*plane.x + minY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
} else {
if (minX*plane.x + maxY*plane.y + minZ*plane.z <= plane.offset) return -1;
if (maxX*plane.x + minY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side);
} else if (plane.z >= 0) {
if (minX*plane.x + minY*plane.y + maxZ*plane.z <= plane.offset) return -1;
if (maxX*plane.x + maxY*plane.y + minZ*plane.z > plane.offset) culling &= (63 & ~side);
} else {
if (minX*plane.x + minY*plane.y + minZ*plane.z <= plane.offset) return -1;
if (maxX*plane.x + maxY*plane.y + maxZ*plane.z > plane.offset) culling &= (63 & ~side);
}
}
side <<= 1;
}
return culling;
}
/**
* @private
*/
alternativa3d function checkOcclusion(occluders:Vector.<Occluder>, occludersLength:int, transform:Transform3D):Boolean {
var ax:Number = transform.a*minX + transform.b*minY + transform.c*minZ + transform.d;
var ay:Number = transform.e*minX + transform.f*minY + transform.g*minZ + transform.h;
var az:Number = transform.i*minX + transform.j*minY + transform.k*minZ + transform.l;
var bx:Number = transform.a*maxX + transform.b*minY + transform.c*minZ + transform.d;
var by:Number = transform.e*maxX + transform.f*minY + transform.g*minZ + transform.h;
var bz:Number = transform.i*maxX + transform.j*minY + transform.k*minZ + transform.l;
var cx:Number = transform.a*minX + transform.b*maxY + transform.c*minZ + transform.d;
var cy:Number = transform.e*minX + transform.f*maxY + transform.g*minZ + transform.h;
var cz:Number = transform.i*minX + transform.j*maxY + transform.k*minZ + transform.l;
var dx:Number = transform.a*maxX + transform.b*maxY + transform.c*minZ + transform.d;
var dy:Number = transform.e*maxX + transform.f*maxY + transform.g*minZ + transform.h;
var dz:Number = transform.i*maxX + transform.j*maxY + transform.k*minZ + transform.l;
var ex:Number = transform.a*minX + transform.b*minY + transform.c*maxZ + transform.d;
var ey:Number = transform.e*minX + transform.f*minY + transform.g*maxZ + transform.h;
var ez:Number = transform.i*minX + transform.j*minY + transform.k*maxZ + transform.l;
var fx:Number = transform.a*maxX + transform.b*minY + transform.c*maxZ + transform.d;
var fy:Number = transform.e*maxX + transform.f*minY + transform.g*maxZ + transform.h;
var fz:Number = transform.i*maxX + transform.j*minY + transform.k*maxZ + transform.l;
var gx:Number = transform.a*minX + transform.b*maxY + transform.c*maxZ + transform.d;
var gy:Number = transform.e*minX + transform.f*maxY + transform.g*maxZ + transform.h;
var gz:Number = transform.i*minX + transform.j*maxY + transform.k*maxZ + transform.l;
var hx:Number = transform.a*maxX + transform.b*maxY + transform.c*maxZ + transform.d;
var hy:Number = transform.e*maxX + transform.f*maxY + transform.g*maxZ + transform.h;
var hz:Number = transform.i*maxX + transform.j*maxY + transform.k*maxZ + transform.l;
for (var i:int = 0; i < occludersLength; i++) {
var occluder:Occluder = occluders[i];
for (var plane:CullingPlane = occluder.planeList; plane != null; plane = plane.next) {
if (plane.x*ax + plane.y*ay + plane.z*az > plane.offset ||
plane.x*bx + plane.y*by + plane.z*bz > plane.offset ||
plane.x*cx + plane.y*cy + plane.z*cz > plane.offset ||
plane.x*dx + plane.y*dy + plane.z*dz > plane.offset ||
plane.x*ex + plane.y*ey + plane.z*ez > plane.offset ||
plane.x*fx + plane.y*fy + plane.z*fz > plane.offset ||
plane.x*gx + plane.y*gy + plane.z*gz > plane.offset ||
plane.x*hx + plane.y*hy + plane.z*hz > plane.offset) break;
}
if (plane == null) return true;
}
return false;
}
/**
* @private
*/
alternativa3d function checkRays(origins:Vector.<Vector3D>, directions:Vector.<Vector3D>, raysLength:int):Boolean {
for (var i:int = 0; i < raysLength; i++) {
var origin:Vector3D = origins[i];
var direction:Vector3D = directions[i];
if (origin.x >= minX && origin.x <= maxX && origin.y >= minY && origin.y <= maxY && origin.z >= minZ && origin.z <= maxZ) return true;
if (origin.x < minX && direction.x <= 0 || origin.x > maxX && direction.x >= 0 || origin.y < minY && direction.y <= 0 || origin.y > maxY && direction.y >= 0 || origin.z < minZ && direction.z <= 0 || origin.z > maxZ && direction.z >= 0) continue;
var a:Number;
var b:Number;
var c:Number;
var d:Number;
var threshold:Number = 0.000001;
// Intersection of X and Y projection
if (direction.x > threshold) {
a = (minX - origin.x)/direction.x;
b = (maxX - origin.x)/direction.x;
} else if (direction.x < -threshold) {
a = (maxX - origin.x)/direction.x;
b = (minX - origin.x)/direction.x;
} else {
a = 0;
b = 1e+22;
}
if (direction.y > threshold) {
c = (minY - origin.y)/direction.y;
d = (maxY - origin.y)/direction.y;
} else if (direction.y < -threshold) {
c = (maxY - origin.y)/direction.y;
d = (minY - origin.y)/direction.y;
} else {
c = 0;
d = 1e+22;
}
if (c >= b || d <= a) continue;
if (c < a) {
if (d < b) b = d;
} else {
a = c;
if (d < b) b = d;
}
// Intersection of XY and Z projections
if (direction.z > threshold) {
c = (minZ - origin.z)/direction.z;
d = (maxZ - origin.z)/direction.z;
} else if (direction.z < -threshold) {
c = (maxZ - origin.z)/direction.z;
d = (minZ - origin.z)/direction.z;
} else {
c = 0;
d = 1e+22;
}
if (c >= b || d <= a) continue;
return true;
}
return false;
}
/**
* @private
*/
alternativa3d function checkSphere(sphere:Vector3D):Boolean {
return sphere.x + sphere.w > minX && sphere.x - sphere.w < maxX && sphere.y + sphere.w > minY && sphere.y - sphere.w < maxY && sphere.z + sphere.w > minZ && sphere.z - sphere.w < maxZ;
}
/**
* Checks if the ray crosses the <code>BoundBox</code>.
*
* @param origin Ray origin.
* @param direction Ray direction.
* @return <code>true</code> if intersection was found and <code>false</code> otherwise.
*/
public function intersectRay(origin:Vector3D, direction:Vector3D):Boolean {
if (origin.x >= minX && origin.x <= maxX && origin.y >= minY && origin.y <= maxY && origin.z >= minZ && origin.z <= maxZ) return true;
if (origin.x < minX && direction.x <= 0) return false;
if (origin.x > maxX && direction.x >= 0) return false;
if (origin.y < minY && direction.y <= 0) return false;
if (origin.y > maxY && direction.y >= 0) return false;
if (origin.z < minZ && direction.z <= 0) return false;
if (origin.z > maxZ && direction.z >= 0) return false;
var a:Number;
var b:Number;
var c:Number;
var d:Number;
var threshold:Number = 0.000001;
// Intersection of X and Y projection
if (direction.x > threshold) {
a = (minX - origin.x) / direction.x;
b = (maxX - origin.x) / direction.x;
} else if (direction.x < -threshold) {
a = (maxX - origin.x) / direction.x;
b = (minX - origin.x) / direction.x;
} else {
a = -1e+22;
b = 1e+22;
}
if (direction.y > threshold) {
c = (minY - origin.y) / direction.y;
d = (maxY - origin.y) / direction.y;
} else if (direction.y < -threshold) {
c = (maxY - origin.y) / direction.y;
d = (minY - origin.y) / direction.y;
} else {
c = -1e+22;
d = 1e+22;
}
if (c >= b || d <= a) return false;
if (c < a) {
if (d < b) b = d;
} else {
a = c;
if (d < b) b = d;
}
// Intersection of XY and Z projections
if (direction.z > threshold) {
c = (minZ - origin.z) / direction.z;
d = (maxZ - origin.z) / direction.z;
} else if (direction.z < -threshold) {
c = (maxZ - origin.z) / direction.z;
d = (minZ - origin.z) / direction.z;
} else {
c = -1e+22;
d = 1e+22;
}
if (c >= b || d <= a) return false;
return true;
}
/**
* Duplicates an instance of <code>BoundBox</code>.
* @return New <code>BoundBox</code> instance with same set of properties.
*/
public function clone():BoundBox {
var res:BoundBox = new BoundBox();
res.minX = minX;
res.minY = minY;
res.minZ = minZ;
res.maxX = maxX;
res.maxY = maxY;
res.maxZ = maxZ;
return res;
}
/**
* Returns a string representation of <code>BoundBox</code>.
* @return A string representation of <code>BoundBox</code>.
*/
public function toString():String {
return "[BoundBox " + "X:[" + minX.toFixed(2) + ", " + maxX.toFixed(2) + "] Y:[" + minY.toFixed(2) + ", " + maxY.toFixed(2) + "] Z:[" + minZ.toFixed(2) + ", " + maxZ.toFixed(2) + "]]";
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,50 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.core {
/**
* @private
*/
public class CullingPlane {
public var x:Number;
public var y:Number;
public var z:Number;
public var offset:Number;
public var next:CullingPlane;
static public var collector:CullingPlane;
static public function create():CullingPlane {
if (collector != null) {
var res:CullingPlane = collector;
collector = res.next;
res.next = null;
return res;
} else {
return new CullingPlane();
}
}
public function create():CullingPlane {
if (collector != null) {
var res:CullingPlane = collector;
collector = res.next;
res.next = null;
return res;
} else {
return new CullingPlane();
}
}
}
}

View File

@@ -0,0 +1,311 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.core {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.objects.WireFrame;
import flash.utils.Dictionary;
use namespace alternativa3d;
/**
* Class stores values, that are passed to camera methods <code>addToDebug()</code> and <code>removeFromDebug()</code>.
*
* @see alternativa.engine3d.core.Camera3D#addToDebug()
* @see alternativa.engine3d.core.Camera3D#removeFromDebug()
*/
public class Debug {
//static public const NAMES:int = 1;
//static public const AXES:int = 2;
//static public const CENTERS:int = 4;
/**
* Display of objects bound boxes.
*/
static public const BOUNDS:int = 8;
/**
* Display of content, that is depended on object type: wireframe for <code>Mesh</code>, schematic display for light sources.
* Now has been implemented the support of not all classes
*/
static public const CONTENT:int = 16;
//static public const VERTICES:int = 32;
//static public const NORMALS:int = 64;
// /**
// * Display of object NODES, that contains tree structure.
// */
// static public const NODES:int = 128;
// /**
// * Display of light sources.
// */
// static public const LIGHTS:int = 256;
// /**
// * Display of objects joints, that contains skeletal hierarchy.
// */
// static public const BONES:int = 512;
static private var boundWires:Dictionary = new Dictionary();
static private function createBoundWire():WireFrame {
var res:WireFrame = new WireFrame();
res.geometry.addLine(-0.5,-0.5,-0.5, 0.5,-0.5,-0.5);
res.geometry.addLine(0.5,-0.5,-0.5, 0.5,0.5,-0.5);
res.geometry.addLine(0.5,0.5,-0.5, -0.5,0.5,-0.5);
res.geometry.addLine(-0.5,0.5,-0.5, -0.5,-0.5,-0.5);
res.geometry.addLine(-0.5,-0.5,0.5, 0.5,-0.5,0.5);
res.geometry.addLine(0.5,-0.5,0.5, 0.5,0.5,0.5);
res.geometry.addLine(0.5,0.5,0.5, -0.5,0.5,0.5);
res.geometry.addLine(-0.5,0.5,0.5, -0.5,-0.5,0.5);
res.geometry.addLine(-0.5,-0.5,-0.5, -0.5,-0.5,0.5);
res.geometry.addLine(0.5,-0.5,-0.5, 0.5,-0.5,0.5);
res.geometry.addLine(0.5,0.5,-0.5, 0.5,0.5,0.5);
res.geometry.addLine(-0.5,0.5,-0.5, -0.5,0.5,0.5);
return res;
}
/**
* @private
*/
static alternativa3d function drawBoundBox(camera:Camera3D, boundBox:BoundBox, transform:Transform3D, color:int = -1):void {
var boundWire:WireFrame = boundWires[camera.context3D];
if (boundWire == null) {
boundWire = createBoundWire();
boundWires[camera.context3D] = boundWire;
boundWire.geometry.upload(camera.context3D);
}
boundWire.color = color >= 0 ? color : 0x99FF00;
boundWire.thickness = 1;
boundWire.transform.compose((boundBox.minX + boundBox.maxX)*0.5, (boundBox.minY + boundBox.maxY)*0.5, (boundBox.minZ + boundBox.maxZ)*0.5, 0, 0, 0, boundBox.maxX - boundBox.minX, boundBox.maxY - boundBox.minY, boundBox.maxZ - boundBox.minZ);
boundWire.localToCameraTransform.combine(transform, boundWire.transform);
boundWire.collectDraws(camera, null, 0);
}
/**
* @private
*/
/*static alternativa3d function drawEdges(camera:Camera3D, canvas:Canvas, list:Face, color:int):void {
var viewSizeX:Number = camera.viewSizeX;
var viewSizeY:Number = camera.viewSizeY;
var t:Number;
canvas.gfx.lineStyle(0, color);
for (var face:Face = list; face != null; face = face.processNext) {
var wrapper:Wrapper = face.wrapper;
var vertex:Vertex = wrapper.vertex;
t = 1/vertex.cameraZ;
var x:Number = vertex.cameraX*viewSizeX*t;
var y:Number = vertex.cameraY*viewSizeY*t;
canvas.gfx.moveTo(x, y);
for (wrapper = wrapper.next; wrapper != null; wrapper = wrapper.next) {
vertex = wrapper.vertex;
t = 1/vertex.cameraZ;
canvas.gfx.lineTo(vertex.cameraX*viewSizeX*t, vertex.cameraY*viewSizeY*t);
}
canvas.gfx.lineTo(x, y);
}
}*/
//static private const boundVertexList:Vertex = Vertex.createList(8);
/**
* @private
*/
/*static alternativa3d function drawBounds(camera:Camera3D, canvas:Canvas, transformation:Object3D, boundMinX:Number, boundMinY:Number, boundMinZ:Number, boundMaxX:Number, boundMaxY:Number, boundMaxZ:Number, color:int = -1, alpha:Number = 1):void {
var vertex:Vertex;
// Fill
var a:Vertex = boundVertexList;
a.x = boundMinX;
a.y = boundMinY;
a.z = boundMinZ;
var b:Vertex = a.next;
b.x = boundMaxX;
b.y = boundMinY;
b.z = boundMinZ;
var c:Vertex = b.next;
c.x = boundMinX;
c.y = boundMaxY;
c.z = boundMinZ;
var d:Vertex = c.next;
d.x = boundMaxX;
d.y = boundMaxY;
d.z = boundMinZ;
var e:Vertex = d.next;
e.x = boundMinX;
e.y = boundMinY;
e.z = boundMaxZ;
var f:Vertex = e.next;
f.x = boundMaxX;
f.y = boundMinY;
f.z = boundMaxZ;
var g:Vertex = f.next;
g.x = boundMinX;
g.y = boundMaxY;
g.z = boundMaxZ;
var h:Vertex = g.next;
h.x = boundMaxX;
h.y = boundMaxY;
h.z = boundMaxZ;
// Transformation to camera
for (vertex = a; vertex != null; vertex = vertex.next) {
vertex.cameraX = transformation.ma*vertex.x + transformation.mb*vertex.y + transformation.mc*vertex.z + transformation.md;
vertex.cameraY = transformation.me*vertex.x + transformation.mf*vertex.y + transformation.mg*vertex.z + transformation.mh;
vertex.cameraZ = transformation.mi*vertex.x + transformation.mj*vertex.y + transformation.mk*vertex.z + transformation.ml;
if (vertex.cameraZ <= 0) return;
}
// Projection
var viewSizeX:Number = camera.viewSizeX;
var viewSizeY:Number = camera.viewSizeY;
for (vertex = a; vertex != null; vertex = vertex.next) {
var t:Number = 1/vertex.cameraZ;
vertex.cameraX = vertex.cameraX*viewSizeX*t;
vertex.cameraY = vertex.cameraY*viewSizeY*t;
}
// Rendering
canvas.gfx.lineStyle(0, (color < 0) ? ((transformation.culling > 0) ? 0xFFFF00 : 0x00FF00) : color, alpha);
canvas.gfx.moveTo(a.cameraX, a.cameraY);
canvas.gfx.lineTo(b.cameraX, b.cameraY);
canvas.gfx.lineTo(d.cameraX, d.cameraY);
canvas.gfx.lineTo(c.cameraX, c.cameraY);
canvas.gfx.lineTo(a.cameraX, a.cameraY);
canvas.gfx.moveTo(e.cameraX, e.cameraY);
canvas.gfx.lineTo(f.cameraX, f.cameraY);
canvas.gfx.lineTo(h.cameraX, h.cameraY);
canvas.gfx.lineTo(g.cameraX, g.cameraY);
canvas.gfx.lineTo(e.cameraX, e.cameraY);
canvas.gfx.moveTo(a.cameraX, a.cameraY);
canvas.gfx.lineTo(e.cameraX, e.cameraY);
canvas.gfx.moveTo(b.cameraX, b.cameraY);
canvas.gfx.lineTo(f.cameraX, f.cameraY);
canvas.gfx.moveTo(d.cameraX, d.cameraY);
canvas.gfx.lineTo(h.cameraX, h.cameraY);
canvas.gfx.moveTo(c.cameraX, c.cameraY);
canvas.gfx.lineTo(g.cameraX, g.cameraY);
}*/
//static private const nodeVertexList:Vertex = Vertex.createList(4);
/**
* @private
*/
/*static alternativa3d function drawKDNode(camera:Camera3D, canvas:Canvas, transformation:Object3D, axis:int, coord:Number, boundMinX:Number, boundMinY:Number, boundMinZ:Number, boundMaxX:Number, boundMaxY:Number, boundMaxZ:Number, alpha:Number):void {
var vertex:Vertex;
// Fill
var a:Vertex = nodeVertexList;
var b:Vertex = a.next;
var c:Vertex = b.next;
var d:Vertex = c.next;
if (axis == 0) {
a.x = coord;
a.y = boundMinY;
a.z = boundMaxZ;
b.x = coord;
b.y = boundMaxY;
b.z = boundMaxZ;
c.x = coord;
c.y = boundMaxY;
c.z = boundMinZ;
d.x = coord;
d.y = boundMinY;
d.z = boundMinZ;
} else if (axis == 1) {
a.x = boundMaxX;
a.y = coord;
a.z = boundMaxZ;
b.x = boundMinX;
b.y = coord;
b.z = boundMaxZ;
c.x = boundMinX;
c.y = coord;
c.z = boundMinZ;
d.x = boundMaxX;
d.y = coord;
d.z = boundMinZ;
} else {
a.x = boundMinX;
a.y = boundMinY;
a.z = coord;
b.x = boundMaxX;
b.y = boundMinY;
b.z = coord;
c.x = boundMaxX;
c.y = boundMaxY;
c.z = coord;
d.x = boundMinX;
d.y = boundMaxY;
d.z = coord;
}
// Transformation to camera
for (vertex = a; vertex != null; vertex = vertex.next) {
vertex.cameraX = transformation.ma*vertex.x + transformation.mb*vertex.y + transformation.mc*vertex.z + transformation.md;
vertex.cameraY = transformation.me*vertex.x + transformation.mf*vertex.y + transformation.mg*vertex.z + transformation.mh;
vertex.cameraZ = transformation.mi*vertex.x + transformation.mj*vertex.y + transformation.mk*vertex.z + transformation.ml;
if (vertex.cameraZ <= 0) return;
}
// Projection
var viewSizeX:Number = camera.viewSizeX;
var viewSizeY:Number = camera.viewSizeY;
for (vertex = a; vertex != null; vertex = vertex.next) {
var t:Number = 1/vertex.cameraZ;
vertex.cameraX = vertex.cameraX*viewSizeX*t;
vertex.cameraY = vertex.cameraY*viewSizeY*t;
}
// Rendering
canvas.gfx.lineStyle(0, (axis == 0) ? 0xFF0000 : ((axis == 1) ? 0x00FF00 : 0x0000FF), alpha);
canvas.gfx.moveTo(a.cameraX, a.cameraY);
canvas.gfx.lineTo(b.cameraX, b.cameraY);
canvas.gfx.lineTo(c.cameraX, c.cameraY);
canvas.gfx.lineTo(d.cameraX, d.cameraY);
canvas.gfx.lineTo(a.cameraX, a.cameraY);
}*/
/**
* @private
*/
/*static alternativa3d function drawBone(canvas:Canvas, x1:Number, y1:Number, x2:Number, y2:Number, size:Number, color:int):void {
var nx:Number = x2 - x1;
var ny:Number = y2 - y1;
var nl:Number = Math.sqrt(nx*nx + ny*ny);
if (nl > 0.001) {
nx /= nl;
ny /= nl;
var lx:Number = ny*size;
var ly:Number = -nx*size;
var rx:Number = -ny*size;
var ry:Number = nx*size;
if (nl > size*2) {
nl = size;
} else {
nl = nl/2;
}
canvas.gfx.lineStyle(1, color);
canvas.gfx.beginFill(color, 0.6);
canvas.gfx.moveTo(x1, y1);
canvas.gfx.lineTo(x1 + nx*nl + lx, y1 + ny*nl + ly);
canvas.gfx.lineTo(x2, y2);
canvas.gfx.lineTo(x1 + nx*nl + rx, y1 + ny*nl + ry);
canvas.gfx.lineTo(x1, y1);
}
}*/
}
}

View File

@@ -0,0 +1,150 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.core {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.materials.ShaderProgram;
import alternativa.engine3d.materials.compiler.Variable;
import alternativa.engine3d.materials.compiler.VariableType;
import flash.utils.Dictionary;
use namespace alternativa3d;
/**
* @private
*/
public class DebugDrawUnit extends DrawUnit {
alternativa3d var shader:ShaderProgram;
alternativa3d var vertexConstantsIndexes:Dictionary = new Dictionary(false);
alternativa3d var fragmentConstantsIndexes:Dictionary = new Dictionary(false);
override alternativa3d function clear():void {
var k:*;
for (k in vertexConstantsIndexes) {
delete vertexConstantsIndexes[k];
}
for (k in fragmentConstantsIndexes) {
delete fragmentConstantsIndexes[k];
}
super.clear();
}
override alternativa3d function setVertexConstantsFromVector(firstRegister:int, data:Vector.<Number>, numRegisters:int):void {
super.setVertexConstantsFromVector(firstRegister, data, numRegisters);
for (var i:int = 0; i < numRegisters; i++) {
vertexConstantsIndexes[int(firstRegister + i)] = true;
}
}
override alternativa3d function setVertexConstantsFromNumbers(firstRegister:int, x:Number, y:Number, z:Number, w:Number = 1):void {
super.setVertexConstantsFromNumbers(firstRegister, x, y, z, w);
vertexConstantsIndexes[firstRegister] = true;
}
override alternativa3d function setVertexConstantsFromTransform(firstRegister:int, transform:Transform3D):void {
super.setVertexConstantsFromTransform(firstRegister, transform);
vertexConstantsIndexes[firstRegister] = true;
vertexConstantsIndexes[int(firstRegister + 1)] = true;
vertexConstantsIndexes[int(firstRegister + 2)] = true;
}
alternativa3d override function setProjectionConstants(camera:Camera3D, firstRegister:int, transform:Transform3D = null):void {
super.setProjectionConstants(camera, firstRegister, transform);
vertexConstantsIndexes[firstRegister] = true;
vertexConstantsIndexes[int(firstRegister + 1)] = true;
vertexConstantsIndexes[int(firstRegister + 2)] = true;
vertexConstantsIndexes[int(firstRegister + 3)] = true;
}
override alternativa3d function setFragmentConstantsFromVector(firstRegister:int, data:Vector.<Number>, numRegisters:int):void {
super.setFragmentConstantsFromVector(firstRegister, data, numRegisters);
for (var i:int = 0; i < numRegisters; i++) {
fragmentConstantsIndexes[int(firstRegister + i)] = true;
}
}
override alternativa3d function setFragmentConstantsFromNumbers(firstRegister:int, x:Number, y:Number, z:Number, w:Number = 1):void {
super.setFragmentConstantsFromNumbers(firstRegister, x, y, z, w);
fragmentConstantsIndexes[firstRegister] = true;
}
override alternativa3d function setFragmentConstantsFromTransform(firstRegister:int, transform:Transform3D):void {
super.setFragmentConstantsFromTransform(firstRegister, transform);
fragmentConstantsIndexes[firstRegister] = true;
fragmentConstantsIndexes[int(firstRegister + 1)] = true;
fragmentConstantsIndexes[int(firstRegister + 2)] = true;
}
public function check():void {
if (object == null) throw new Error("Object not set.");
if (program == null) throw new Error("Program not set.");
if (indexBuffer == null) throw new Error("IndexBuffer not set.");
if (shader == null) return;
var index:int;
var variable:Variable;
for each (variable in shader.vertexShader._linkedVariables) {
index = variable.index;
if (index >= 0) {
switch (variable.type) {
case VariableType.ATTRIBUTE:
if (!hasVertexBuffer(index)) {
throw new Error("VertexBuffer " + index + " with variable name " + variable.name + " not set.");
}
break;
case VariableType.CONSTANT:
if (!vertexConstantsIndexes[index]) {
throw new Error("Vertex Constant " + index + " with variable name " + variable.name + " not set.");
}
break;
}
}
}
for each (variable in shader.fragmentShader._linkedVariables) {
index = variable.index;
if (index >= 0) {
switch (variable.type) {
case VariableType.SAMPLER:
if (!hasTexture(index)) {
throw new Error("Sampler " + index + " with variable name " + variable.name + " not set.");
}
break;
case VariableType.CONSTANT:
if (!fragmentConstantsIndexes[index]) {
throw new Error("Fragment Constant " + index + " with variable name " + variable.name + " not set.");
}
break;
}
}
}
}
private function hasVertexBuffer(index:int):Boolean {
for (var i:int = 0; i < vertexBuffersLength; i++) {
if (vertexBuffersIndexes[i] == index) {
return true;
}
}
return false;
}
private function hasTexture(index:int):Boolean {
for (var i:int = 0; i < texturesLength; i++) {
if (texturesSamplers[i] == index) {
return true;
}
}
return false;
}
}
}

View File

@@ -0,0 +1,50 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.core {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.materials.ShaderProgram;
import flash.display3D.IndexBuffer3D;
import flash.display3D.Program3D;
use namespace alternativa3d;
/**
* @private
*/
public class DebugMaterialsRenderer extends Renderer {
override alternativa3d function createDrawUnit(object:Object3D, program:Program3D, indexBuffer:IndexBuffer3D, firstIndex:int, numTriangles:int, debugShader:ShaderProgram = null):DrawUnit {
var res:DebugDrawUnit;
if (collector != null) {
res = DebugDrawUnit(collector);
collector = collector.next;
res.next = null;
} else {
res = new DebugDrawUnit();
}
res.shader = debugShader;
res.object = object;
res.program = program;
res.indexBuffer = indexBuffer;
res.firstIndex = firstIndex;
res.numTriangles = numTriangles;
return res;
}
override alternativa3d function addDrawUnit(drawUnit:DrawUnit, renderPriority:int):void {
DebugDrawUnit(drawUnit).check();
super.addDrawUnit(drawUnit, renderPriority);
}
}
}

View File

@@ -0,0 +1,248 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.core {
import alternativa.engine3d.alternativa3d;
import flash.display3D.Context3DBlendFactor;
import flash.display3D.Context3DTriangleFace;
import flash.display3D.IndexBuffer3D;
import flash.display3D.Program3D;
import flash.display3D.VertexBuffer3D;
import flash.display3D.textures.TextureBase;
use namespace alternativa3d;
/**
* @private
*/
public class DrawUnit {
alternativa3d var next:DrawUnit;
// Required parameters
alternativa3d var object:Object3D;
alternativa3d var program:Program3D;
alternativa3d var indexBuffer:IndexBuffer3D;
alternativa3d var firstIndex:int;
alternativa3d var numTriangles:int;
// Additional parameters
alternativa3d var blendSource:String = Context3DBlendFactor.ONE;
alternativa3d var blendDestination:String = Context3DBlendFactor.ZERO;
alternativa3d var culling:String = Context3DTriangleFace.FRONT;
// Textures
alternativa3d var textures:Vector.<TextureBase> = new Vector.<TextureBase>();
alternativa3d var texturesSamplers:Vector.<int> = new Vector.<int>();
alternativa3d var texturesLength:int = 0;
// Vertex buffers
alternativa3d var vertexBuffers:Vector.<VertexBuffer3D> = new Vector.<VertexBuffer3D>();
alternativa3d var vertexBuffersIndexes:Vector.<int> = new Vector.<int>();
alternativa3d var vertexBuffersOffsets:Vector.<int> = new Vector.<int>();
alternativa3d var vertexBuffersFormats:Vector.<String> = new Vector.<String>();
alternativa3d var vertexBuffersLength:int = 0;
// Constants
alternativa3d var vertexConstants:Vector.<Number> = new Vector.<Number>();
alternativa3d var vertexConstantsRegistersCount:int = 0;
alternativa3d var fragmentConstants:Vector.<Number> = new Vector.<Number>(28*4, true);
alternativa3d var fragmentConstantsRegistersCount:int = 0;
public function DrawUnit() {
}
alternativa3d function clear():void {
object = null;
program = null;
indexBuffer = null;
blendSource = Context3DBlendFactor.ONE;
blendDestination = Context3DBlendFactor.ZERO;
culling = Context3DTriangleFace.FRONT;
textures.length = 0;
texturesLength = 0;
vertexBuffers.length = 0;
vertexBuffersLength = 0;
vertexConstantsRegistersCount = 0;
fragmentConstantsRegistersCount = 0;
}
alternativa3d function setTextureAt(sampler:int, texture:TextureBase):void {
if (uint(sampler) > 8) throw new Error("Sampler index " + sampler + " is out of bounds.");
if (texture == null) throw new Error("Texture is null");
texturesSamplers[texturesLength] = sampler;
textures[texturesLength] = texture;
texturesLength++;
}
alternativa3d function setVertexBufferAt(index:int, buffer:VertexBuffer3D, bufferOffset:int, format:String):void {
if (uint(index) > 8) throw new Error("VertexBuffer index " + index + " is out of bounds.");
if (buffer == null) throw new Error("Buffer is null");
vertexBuffersIndexes[vertexBuffersLength] = index;
vertexBuffers[vertexBuffersLength] = buffer;
vertexBuffersOffsets[vertexBuffersLength] = bufferOffset;
vertexBuffersFormats[vertexBuffersLength] = format;
vertexBuffersLength++;
}
alternativa3d function setVertexConstantsFromVector(firstRegister:int, data:Vector.<Number>, numRegisters:int):void {
if (uint(firstRegister) > (128 - numRegisters)) throw new Error("Register index " + firstRegister + " is out of bounds.");
var offset:int = firstRegister << 2;
if (firstRegister + numRegisters > vertexConstantsRegistersCount) {
vertexConstantsRegistersCount = firstRegister + numRegisters;
vertexConstants.length = vertexConstantsRegistersCount << 2;
}
for (var i:int = 0, len:int = numRegisters << 2; i < len; i++) {
vertexConstants[offset] = data[i];
offset++;
}
}
alternativa3d function setVertexConstantsFromNumbers(firstRegister:int, x:Number, y:Number, z:Number, w:Number = 1):void {
if (uint(firstRegister) > 127) throw new Error("Register index " + firstRegister + " is out of bounds.");
var offset:int = firstRegister << 2;
if (firstRegister + 1 > vertexConstantsRegistersCount) {
vertexConstantsRegistersCount = firstRegister + 1;
vertexConstants.length = vertexConstantsRegistersCount << 2;
}
vertexConstants[offset] = x; offset++;
vertexConstants[offset] = y; offset++;
vertexConstants[offset] = z; offset++;
vertexConstants[offset] = w;
}
alternativa3d function setVertexConstantsFromTransform(firstRegister:int, transform:Transform3D):void {
if (uint(firstRegister) > 125) throw new Error("Register index " + firstRegister + " is out of bounds.");
var offset:int = firstRegister << 2;
if (firstRegister + 3 > vertexConstantsRegistersCount) {
vertexConstantsRegistersCount = firstRegister + 3;
vertexConstants.length = vertexConstantsRegistersCount << 2;
}
vertexConstants[offset] = transform.a; offset++;
vertexConstants[offset] = transform.b; offset++;
vertexConstants[offset] = transform.c; offset++;
vertexConstants[offset] = transform.d; offset++;
vertexConstants[offset] = transform.e; offset++;
vertexConstants[offset] = transform.f; offset++;
vertexConstants[offset] = transform.g; offset++;
vertexConstants[offset] = transform.h; offset++;
vertexConstants[offset] = transform.i; offset++;
vertexConstants[offset] = transform.j; offset++;
vertexConstants[offset] = transform.k; offset++;
vertexConstants[offset] = transform.l;
}
/**
* @private
*/
alternativa3d function setProjectionConstants(camera:Camera3D, firstRegister:int, transform:Transform3D = null):void {
if (uint(firstRegister) > 124) throw new Error("Register index is out of bounds.");
var offset:int = firstRegister << 2;
if (firstRegister + 4 > vertexConstantsRegistersCount) {
vertexConstantsRegistersCount = firstRegister + 4;
vertexConstants.length = vertexConstantsRegistersCount << 2;
}
if (transform != null) {
vertexConstants[offset] = transform.a*camera.m0; offset++;
vertexConstants[offset] = transform.b*camera.m0; offset++;
vertexConstants[offset] = transform.c*camera.m0; offset++;
vertexConstants[offset] = transform.d*camera.m0; offset++;
vertexConstants[offset] = transform.e*camera.m5; offset++;
vertexConstants[offset] = transform.f*camera.m5; offset++;
vertexConstants[offset] = transform.g*camera.m5; offset++;
vertexConstants[offset] = transform.h*camera.m5; offset++;
vertexConstants[offset] = transform.i*camera.m10; offset++;
vertexConstants[offset] = transform.j*camera.m10; offset++;
vertexConstants[offset] = transform.k*camera.m10; offset++;
vertexConstants[offset] = transform.l*camera.m10 + camera.m14; offset++;
if (!camera.orthographic) {
vertexConstants[offset] = transform.i; offset++;
vertexConstants[offset] = transform.j; offset++;
vertexConstants[offset] = transform.k; offset++;
vertexConstants[offset] = transform.l;
} else {
vertexConstants[offset] = 0; offset++;
vertexConstants[offset] = 0; offset++;
vertexConstants[offset] = 0; offset++;
vertexConstants[offset] = 1;
}
} else {
vertexConstants[offset] = camera.m0; offset++;
vertexConstants[offset] = 0; offset++;
vertexConstants[offset] = 0; offset++;
vertexConstants[offset] = 0; offset++;
vertexConstants[offset] = 0; offset++;
vertexConstants[offset] = camera.m5; offset++;
vertexConstants[offset] = 0; offset++;
vertexConstants[offset] = 0; offset++;
vertexConstants[offset] = 0; offset++;
vertexConstants[offset] = 0; offset++;
vertexConstants[offset] = camera.m10; offset++;
vertexConstants[offset] = camera.m14; offset++;
vertexConstants[offset] = 0; offset++;
vertexConstants[offset] = 0; offset++;
if (!camera.orthographic) {
vertexConstants[offset] = 1; offset++;
vertexConstants[offset] = 0;
} else {
vertexConstants[offset] = 0; offset++;
vertexConstants[offset] = 1;
}
}
}
alternativa3d function setFragmentConstantsFromVector(firstRegister:int, data:Vector.<Number>, numRegisters:int):void {
if (uint(firstRegister) > (28 - numRegisters)) throw new Error("Register index " + firstRegister + " is out of bounds.");
var offset:int = firstRegister << 2;
if (firstRegister + numRegisters > fragmentConstantsRegistersCount) {
fragmentConstantsRegistersCount = firstRegister + numRegisters;
}
for (var i:int = 0, len:int = numRegisters << 2; i < len; i++) {
fragmentConstants[offset] = data[i];
offset++;
}
}
alternativa3d function setFragmentConstantsFromNumbers(firstRegister:int, x:Number, y:Number, z:Number, w:Number = 1):void {
if (uint(firstRegister) > 27) throw new Error("Register index " + firstRegister + " is out of bounds.");
var offset:int = firstRegister << 2;
if (firstRegister + 1 > fragmentConstantsRegistersCount) {
fragmentConstantsRegistersCount = firstRegister + 1;
}
fragmentConstants[offset] = x; offset++;
fragmentConstants[offset] = y; offset++;
fragmentConstants[offset] = z; offset++;
fragmentConstants[offset] = w;
}
alternativa3d function setFragmentConstantsFromTransform(firstRegister:int, transform:Transform3D):void {
if (uint(firstRegister) > 25) throw new Error("Register index " + firstRegister + " is out of bounds.");
var offset:int = firstRegister << 2;
if (firstRegister + 3 > fragmentConstantsRegistersCount) {
fragmentConstantsRegistersCount = firstRegister + 3;
}
fragmentConstants[offset] = transform.a; offset++;
fragmentConstants[offset] = transform.b; offset++;
fragmentConstants[offset] = transform.c; offset++;
fragmentConstants[offset] = transform.d; offset++;
fragmentConstants[offset] = transform.e; offset++;
fragmentConstants[offset] = transform.f; offset++;
fragmentConstants[offset] = transform.g; offset++;
fragmentConstants[offset] = transform.h; offset++;
fragmentConstants[offset] = transform.i; offset++;
fragmentConstants[offset] = transform.j; offset++;
fragmentConstants[offset] = transform.k; offset++;
fragmentConstants[offset] = transform.l;
}
}
}

View File

@@ -0,0 +1,116 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.core {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.shadows.Shadow;
use namespace alternativa3d;
/**
* Base class for light sources. Light sources are involved in the hierarchy of 3d objects,
* have transformation and bounding boxes (<code>BoundBox</code>).
* Light source influences on objects, boundboxes of which intersect with boundbox of the given light source.
*
* Light3D does not meant for instantiating, use subclasses instead.
*
* @see alternativa.engine3d.core.BoundBox
*/
public class Light3D extends Object3D {
public var shadow:Shadow;
/**
* Color of the light.
*/
public var color:uint;
/**
* Intensity.
*/
public var intensity:Number = 1;
/**
* @private
*/
alternativa3d var lightToObjectTransform:Transform3D = new Transform3D();
/**
* @private
*/
alternativa3d var lightID:String;
/**
* @private
*/
alternativa3d var red:Number;
/**
* @private
*/
alternativa3d var green:Number;
/**
* @private
*/
alternativa3d var blue:Number;
/**
* @private
*/
private static var lastLightNumber:uint = 0;
/**
* @private
*/
public function Light3D() {
lightID = "l" + lastLightNumber.toString(16);
name = "L" + (lastLightNumber++).toString();
}
/**
* @private
*/
override alternativa3d function calculateVisibility(camera:Camera3D):void {
if (intensity != 0 && color > 0) {
camera.lights[camera.lightsLength] = this;
camera.lightsLength++;
}
}
/**
* @private
* Check if given object placed in field of influence of the light.
* @param targetObject Object for checking.
* @return True
*/
alternativa3d function checkBound(targetObject:Object3D):Boolean {
// this check is implemented in subclasses
return true;
}
/**
* @inheritDoc
*/
override public function clone():Object3D {
var res:Light3D = new Light3D();
res.clonePropertiesFrom(this);
return res;
}
/**
* @inheritDoc
*/
override protected function clonePropertiesFrom(source:Object3D):void {
super.clonePropertiesFrom(source);
var src:Light3D = source as Light3D;
color = src.color;
intensity = src.intensity;
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,59 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.core {
import alternativa.engine3d.objects.Surface;
import flash.geom.Point;
import flash.geom.Vector3D;
/**
* A result of searching for intersection of an Object3D and a ray with intersectRay() method of Object3D.
*
* @see Object3D#intersectRay()
*/
public class RayIntersectionData {
/**
* First object intersected by the ray.
*/
public var object:Object3D;
/**
* The point of intersection il local coordinates of object.
*/
public var point:Vector3D;
/**
* Surface of <code>object</code> on which intersection occurred.
*/
public var surface:Surface;
/**
* Distance from ray's origin to intersection point expressed in length of <code>localDirection</code> vector.
*/
public var time:Number;
/**
* Texture coordinates of intersection point.
*/
public var uv:Point;
/**
* Returns the string representation of the specified object.
* @return The string representation of the specified object.
*/
public function toString():String {
return "[RayIntersectionData " + object + ", " + point + ", " + uv + ", " + time + "]";
}
}
}

View File

@@ -0,0 +1,238 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.core {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.materials.ShaderProgram;
import flash.display3D.Context3D;
import flash.display3D.Context3DCompareMode;
import flash.display3D.Context3DProgramType;
import flash.display3D.IndexBuffer3D;
import flash.display3D.Program3D;
import flash.utils.Dictionary;
use namespace alternativa3d;
/**
* @private
*/
public class Renderer {
public static const SKY:int = 10;
public static const OPAQUE:int = 20;
public static const DECALS:int = 30;
public static const TRANSPARENT_SORT:int = 40;
public static const NEXT_LAYER:int = 50;
// Collector
protected var collector:DrawUnit;
alternativa3d var camera:Camera3D;
alternativa3d var drawUnits:Vector.<DrawUnit> = new Vector.<DrawUnit>();
// Key - context, value - properties.
protected static var properties:Dictionary = new Dictionary(true);
protected var _context3D:Context3D;
protected var _contextProperties:RendererContext3DProperties;
alternativa3d function render(context3D:Context3D):void {
updateContext3D(context3D);
var drawUnitsLength:int = drawUnits.length;
for (var i:int = 0; i < drawUnitsLength; i++) {
var list:DrawUnit = drawUnits[i];
if (list != null) {
switch (i) {
case SKY:
_context3D.setDepthTest(false, Context3DCompareMode.ALWAYS);
break;
case OPAQUE:
_context3D.setDepthTest(true, Context3DCompareMode.LESS);
break;
case DECALS:
_context3D.setDepthTest(false, Context3DCompareMode.LESS_EQUAL);
break;
case TRANSPARENT_SORT:
if (list.next != null) list = sortByAverageZ(list);
_context3D.setDepthTest(false, Context3DCompareMode.LESS);
break;
case NEXT_LAYER:
_context3D.setDepthTest(false, Context3DCompareMode.ALWAYS);
break;
}
// Rendering
while (list != null) {
var next:DrawUnit = list.next;
renderDrawUnit(list, _context3D, camera);
// Send to collector
list.clear();
list.next = collector;
collector = list;
list = next;
}
}
}
// Clear
drawUnits.length = 0;
}
alternativa3d function createDrawUnit(object:Object3D, program:Program3D, indexBuffer:IndexBuffer3D, firstIndex:int, numTriangles:int, debugShader:ShaderProgram = null):DrawUnit {
var res:DrawUnit;
if (collector != null) {
res = collector;
collector = collector.next;
res.next = null;
} else {
//trace("new DrawUnit");
res = new DrawUnit();
}
res.object = object;
res.program = program;
res.indexBuffer = indexBuffer;
res.firstIndex = firstIndex;
res.numTriangles = numTriangles;
return res;
}
alternativa3d function addDrawUnit(drawUnit:DrawUnit, renderPriority:int):void {
// Increase array of priorities, if it is necessary
if (renderPriority >= drawUnits.length) drawUnits.length = renderPriority + 1;
// Add
drawUnit.next = drawUnits[renderPriority];
drawUnits[renderPriority] = drawUnit;
}
protected function renderDrawUnit(drawUnit:DrawUnit, context:Context3D, camera:Camera3D):void {
context.setBlendFactors(drawUnit.blendSource, drawUnit.blendDestination);
context.setCulling(drawUnit.culling);
var _usedBuffers:uint = _contextProperties.usedBuffers;
var _usedTextures:uint = _contextProperties.usedTextures;
var bufferIndex:int;
var bufferBit:int;
var currentBuffers:int;
var textureSampler:int;
var textureBit:int;
var currentTextures:int;
for (var i:int = 0; i < drawUnit.vertexBuffersLength; i++) {
bufferIndex = drawUnit.vertexBuffersIndexes[i];
bufferBit = 1 << bufferIndex;
currentBuffers |= bufferBit;
_usedBuffers &= ~bufferBit;
context.setVertexBufferAt(bufferIndex, drawUnit.vertexBuffers[i], drawUnit.vertexBuffersOffsets[i], drawUnit.vertexBuffersFormats[i]);
}
if (drawUnit.vertexConstantsRegistersCount > 0) {
context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 0, drawUnit.vertexConstants, drawUnit.vertexConstantsRegistersCount);
}
if (drawUnit.fragmentConstantsRegistersCount > 0) {
context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, drawUnit.fragmentConstants, drawUnit.fragmentConstantsRegistersCount);
}
for (i = 0; i < drawUnit.texturesLength; i++) {
textureSampler = drawUnit.texturesSamplers[i];
textureBit = 1 << textureSampler;
currentTextures |= textureBit;
_usedTextures &= ~textureBit;
context.setTextureAt(textureSampler, drawUnit.textures[i]);
}
context.setProgram(drawUnit.program);
for (bufferIndex = 0; _usedBuffers > 0; bufferIndex++) {
bufferBit = _usedBuffers & 1;
_usedBuffers >>= 1;
if (bufferBit) context.setVertexBufferAt(bufferIndex, null);
}
for (textureSampler = 0; _usedTextures > 0; textureSampler++) {
textureBit = _usedTextures & 1;
_usedTextures >>= 1;
if (textureBit) context.setTextureAt(textureSampler, null);
}
context.drawTriangles(drawUnit.indexBuffer, drawUnit.firstIndex, drawUnit.numTriangles);
_contextProperties.usedBuffers = currentBuffers;
_contextProperties.usedTextures = currentTextures;
camera.numDraws++;
camera.numTriangles += drawUnit.numTriangles;
}
protected function updateContext3D(value:Context3D):void {
if (_context3D != value) {
_contextProperties = properties[value];
if (_contextProperties == null) {
_contextProperties = new RendererContext3DProperties();
properties[value] = _contextProperties;
}
_context3D = value;
}
}
alternativa3d function sortByAverageZ(list:DrawUnit, direction:Boolean = true):DrawUnit {
var left:DrawUnit = list;
var right:DrawUnit = list.next;
while (right != null && right.next != null) {
list = list.next;
right = right.next.next;
}
right = list.next;
list.next = null;
if (left.next != null) {
left = sortByAverageZ(left, direction);
}
if (right.next != null) {
right = sortByAverageZ(right, direction);
}
var flag:Boolean = direction ? (left.object.localToCameraTransform.l > right.object.localToCameraTransform.l) : (left.object.localToCameraTransform.l < right.object.localToCameraTransform.l);
if (flag) {
list = left;
left = left.next;
} else {
list = right;
right = right.next;
}
var last:DrawUnit = list;
while (true) {
if (left == null) {
last.next = right;
return list;
} else if (right == null) {
last.next = left;
return list;
}
if (flag) {
if (direction ? (left.object.localToCameraTransform.l > right.object.localToCameraTransform.l) : (left.object.localToCameraTransform.l < right.object.localToCameraTransform.l)) {
last = left;
left = left.next;
} else {
last.next = right;
last = right;
right = right.next;
flag = false;
}
} else {
if (direction ? (left.object.localToCameraTransform.l < right.object.localToCameraTransform.l) : (left.object.localToCameraTransform.l > right.object.localToCameraTransform.l)) {
last = right;
right = right.next;
} else {
last.next = left;
last = left;
left = left.next;
flag = true;
}
}
}
return null;
}
}
}

View File

@@ -0,0 +1,23 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.core {
/**
* @private
* Stores settings of context.
*/
public class RendererContext3DProperties {
public var usedBuffers:uint = 0;
public var usedTextures:uint = 0;
}
}

View File

@@ -0,0 +1,57 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.core {
import alternativa.engine3d.alternativa3d;
import flash.display3D.Context3D;
use namespace alternativa3d;
/**
* Base class for GPU data. GPU data can be divided in 2 groups: geometry data and texture data.
* The type of resources for uploading geometry data in GPU is Geometry.
* <code>BitmapTextureResource</code> allows to use textures of type is <code>BitmapData</code> and <code>ATFTextureResource</code> deals with <code>ByteArray</code> consists of ATF data,
* <code>ExternalTextureResource</code> should be used with <code>TexturesLoader</code>, which loads textures from files and automatically uploads in GPU.
*
*
* @see alternativa.engine3d.resources.Geometry
* @see alternativa.engine3d.resources.TextureResource
* @see alternativa.engine3d.resources.BitmapTextureResource
* @see alternativa.engine3d.resources.ATFTextureResource
* @see alternativa.engine3d.resources.ExternalTextureResource
*/
public class Resource {
/**
* Defines if this resource is uploaded inti a <code>Context3D</code>.
*/
public function get isUploaded():Boolean {
return false;
}
/**
* Uploads resource into given <code>Context3D</code>.
*
* @param context3D <code>Context3D</code> to which resource will uploaded.
*/
public function upload(context3D:Context3D):void {
throw new Error("Cannot upload without data");
}
/**
* Removes this resource from <code>Context3D</code> to which it was uploaded.
*/
public function dispose():void {
}
}
}

View File

@@ -0,0 +1,269 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.core {
import alternativa.engine3d.alternativa3d;
use namespace alternativa3d;
/**
* @private
*/
public class Transform3D {
public var a:Number = 1;
public var b:Number = 0;
public var c:Number = 0;
public var d:Number = 0;
public var e:Number = 0;
public var f:Number = 1;
public var g:Number = 0;
public var h:Number = 0;
public var i:Number = 0;
public var j:Number = 0;
public var k:Number = 1;
public var l:Number = 0;
public function identity():void {
a = 1;
b = 0;
c = 0;
d = 0;
e = 0;
f = 1;
g = 0;
h = 0;
i = 0;
j = 0;
k = 1;
l = 0;
}
public function compose(x:Number, y:Number, z:Number, rotationX:Number, rotationY:Number, rotationZ:Number, scaleX:Number, scaleY:Number, scaleZ:Number):void {
var cosX:Number = Math.cos(rotationX);
var sinX:Number = Math.sin(rotationX);
var cosY:Number = Math.cos(rotationY);
var sinY:Number = Math.sin(rotationY);
var cosZ:Number = Math.cos(rotationZ);
var sinZ:Number = Math.sin(rotationZ);
var cosZsinY:Number = cosZ*sinY;
var sinZsinY:Number = sinZ*sinY;
var cosYscaleX:Number = cosY*scaleX;
var sinXscaleY:Number = sinX*scaleY;
var cosXscaleY:Number = cosX*scaleY;
var cosXscaleZ:Number = cosX*scaleZ;
var sinXscaleZ:Number = sinX*scaleZ;
a = cosZ*cosYscaleX;
b = cosZsinY*sinXscaleY - sinZ*cosXscaleY;
c = cosZsinY*cosXscaleZ + sinZ*sinXscaleZ;
d = x;
e = sinZ*cosYscaleX;
f = sinZsinY*sinXscaleY + cosZ*cosXscaleY;
g = sinZsinY*cosXscaleZ - cosZ*sinXscaleZ;
h = y;
i = -sinY*scaleX;
j = cosY*sinXscaleY;
k = cosY*cosXscaleZ;
l = z;
}
public function composeInverse(x:Number, y:Number, z:Number, rotationX:Number, rotationY:Number, rotationZ:Number, scaleX:Number, scaleY:Number, scaleZ:Number):void {
var cosX:Number = Math.cos(rotationX);
var sinX:Number = Math.sin(-rotationX);
var cosY:Number = Math.cos(rotationY);
var sinY:Number = Math.sin(-rotationY);
var cosZ:Number = Math.cos(rotationZ);
var sinZ:Number = Math.sin(-rotationZ);
var sinXsinY:Number = sinX*sinY;
var cosYscaleX:Number = cosY/scaleX;
var cosXscaleY:Number = cosX/scaleY;
var sinXscaleZ:Number = sinX/scaleZ;
var cosXscaleZ:Number = cosX/scaleZ;
a = cosZ*cosYscaleX;
b = -sinZ*cosYscaleX;
c = sinY/scaleX;
d = -a*x - b*y - c*z;
e = sinZ*cosXscaleY + sinXsinY*cosZ/scaleY;
f = cosZ*cosXscaleY - sinXsinY*sinZ/scaleY;
g = -sinX*cosY/scaleY;
h = -e*x - f*y - g*z;
i = sinZ*sinXscaleZ - cosZ*sinY*cosXscaleZ;
j = cosZ*sinXscaleZ + sinY*sinZ*cosXscaleZ;
k = cosY*cosXscaleZ;
l = -i*x - j*y - k*z;
}
public function invert():void {
var ta:Number = a;
var tb:Number = b;
var tc:Number = c;
var td:Number = d;
var te:Number = e;
var tf:Number = f;
var tg:Number = g;
var th:Number = h;
var ti:Number = i;
var tj:Number = j;
var tk:Number = k;
var tl:Number = l;
var det:Number = 1/(-tc*tf*ti + tb*tg*ti + tc*te*tj - ta*tg*tj - tb*te*tk + ta*tf*tk);
a = (-tg*tj + tf*tk)*det;
b = (tc*tj - tb*tk)*det;
c = (-tc*tf + tb*tg)*det;
d = (td*tg*tj - tc*th*tj - td*tf*tk + tb*th*tk + tc*tf*tl - tb*tg*tl)*det;
e = (tg*ti - te*tk)*det;
f = (-tc*ti + ta*tk)*det;
g = (tc*te - ta*tg)*det;
h = (tc*th*ti - td*tg*ti + td*te*tk - ta*th*tk - tc*te*tl + ta*tg*tl)*det;
i = (-tf*ti + te*tj)*det;
j = (tb*ti - ta*tj)*det;
k = (-tb*te + ta*tf)*det;
l = (td*tf*ti - tb*th*ti - td*te*tj + ta*th*tj + tb*te*tl - ta*tf*tl)*det;
}
public function initFromVector(vector:Vector.<Number>):void {
a = vector[0];
b = vector[1];
c = vector[2];
d = vector[3];
e = vector[4];
f = vector[5];
g = vector[6];
h = vector[7];
i = vector[8];
j = vector[9];
k = vector[10];
l = vector[11];
}
public function append(transform:Transform3D):void {
var ta:Number = a;
var tb:Number = b;
var tc:Number = c;
var td:Number = d;
var te:Number = e;
var tf:Number = f;
var tg:Number = g;
var th:Number = h;
var ti:Number = i;
var tj:Number = j;
var tk:Number = k;
var tl:Number = l;
a = transform.a*ta + transform.b*te + transform.c*ti;
b = transform.a*tb + transform.b*tf + transform.c*tj;
c = transform.a*tc + transform.b*tg + transform.c*tk;
d = transform.a*td + transform.b*th + transform.c*tl + transform.d;
e = transform.e*ta + transform.f*te + transform.g*ti;
f = transform.e*tb + transform.f*tf + transform.g*tj;
g = transform.e*tc + transform.f*tg + transform.g*tk;
h = transform.e*td + transform.f*th + transform.g*tl + transform.h;
i = transform.i*ta + transform.j*te + transform.k*ti;
j = transform.i*tb + transform.j*tf + transform.k*tj;
k = transform.i*tc + transform.j*tg + transform.k*tk;
l = transform.i*td + transform.j*th + transform.k*tl + transform.l;
}
public function prepend(transform:Transform3D):void {
var ta:Number = a;
var tb:Number = b;
var tc:Number = c;
var td:Number = d;
var te:Number = e;
var tf:Number = f;
var tg:Number = g;
var th:Number = h;
var ti:Number = i;
var tj:Number = j;
var tk:Number = k;
var tl:Number = l;
a = ta*transform.a + tb*transform.e + tc*transform.i;
b = ta*transform.b + tb*transform.f + tc*transform.j;
c = ta*transform.c + tb*transform.g + tc*transform.k;
d = ta*transform.d + tb*transform.h + tc*transform.l + td;
e = te*transform.a + tf*transform.e + tg*transform.i;
f = te*transform.b + tf*transform.f + tg*transform.j;
g = te*transform.c + tf*transform.g + tg*transform.k;
h = te*transform.d + tf*transform.h + tg*transform.l + th;
i = ti*transform.a + tj*transform.e + tk*transform.i;
j = ti*transform.b + tj*transform.f + tk*transform.j;
k = ti*transform.c + tj*transform.g + tk*transform.k;
l = ti*transform.d + tj*transform.h + tk*transform.l + tl;
}
public function combine(transformA:Transform3D, transformB:Transform3D):void {
a = transformA.a*transformB.a + transformA.b*transformB.e + transformA.c*transformB.i;
b = transformA.a*transformB.b + transformA.b*transformB.f + transformA.c*transformB.j;
c = transformA.a*transformB.c + transformA.b*transformB.g + transformA.c*transformB.k;
d = transformA.a*transformB.d + transformA.b*transformB.h + transformA.c*transformB.l + transformA.d;
e = transformA.e*transformB.a + transformA.f*transformB.e + transformA.g*transformB.i;
f = transformA.e*transformB.b + transformA.f*transformB.f + transformA.g*transformB.j;
g = transformA.e*transformB.c + transformA.f*transformB.g + transformA.g*transformB.k;
h = transformA.e*transformB.d + transformA.f*transformB.h + transformA.g*transformB.l + transformA.h;
i = transformA.i*transformB.a + transformA.j*transformB.e + transformA.k*transformB.i;
j = transformA.i*transformB.b + transformA.j*transformB.f + transformA.k*transformB.j;
k = transformA.i*transformB.c + transformA.j*transformB.g + transformA.k*transformB.k;
l = transformA.i*transformB.d + transformA.j*transformB.h + transformA.k*transformB.l + transformA.l;
}
public function calculateInversion(source:Transform3D):void {
var ta:Number = source.a;
var tb:Number = source.b;
var tc:Number = source.c;
var td:Number = source.d;
var te:Number = source.e;
var tf:Number = source.f;
var tg:Number = source.g;
var th:Number = source.h;
var ti:Number = source.i;
var tj:Number = source.j;
var tk:Number = source.k;
var tl:Number = source.l;
var det:Number = 1/(-tc*tf*ti + tb*tg*ti + tc*te*tj - ta*tg*tj - tb*te*tk + ta*tf*tk);
a = (-tg*tj + tf*tk)*det;
b = (tc*tj - tb*tk)*det;
c = (-tc*tf + tb*tg)*det;
d = (td*tg*tj - tc*th*tj - td*tf*tk + tb*th*tk + tc*tf*tl - tb*tg*tl)*det;
e = (tg*ti - te*tk)*det;
f = (-tc*ti + ta*tk)*det;
g = (tc*te - ta*tg)*det;
h = (tc*th*ti - td*tg*ti + td*te*tk - ta*th*tk - tc*te*tl + ta*tg*tl)*det;
i = (-tf*ti + te*tj)*det;
j = (tb*ti - ta*tj)*det;
k = (-tb*te + ta*tf)*det;
l = (td*tf*ti - tb*th*ti - td*te*tj + ta*th*tj + tb*te*tl - ta*tf*tl)*det;
}
public function copy(source:Transform3D):void {
a = source.a;
b = source.b;
c = source.c;
d = source.d;
e = source.e;
f = source.f;
g = source.g;
h = source.h;
i = source.i;
j = source.j;
k = source.k;
l = source.l;
}
public function toString():String {
return "[Transform3D" +
" a:" + a.toFixed(3) + " b:" + b.toFixed(3) + " c:" + a.toFixed(3) + " d:" + d.toFixed(3) +
" e:" + e.toFixed(3) + " f:" + f.toFixed(3) + " g:" + a.toFixed(3) + " h:" + h.toFixed(3) +
" i:" + i.toFixed(3) + " j:" + j.toFixed(3) + " k:" + a.toFixed(3) + " l:" + l.toFixed(3) + "]";
}
}
}

View File

@@ -0,0 +1,112 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.core {
import alternativa.engine3d.alternativa3d;
import flash.display3D.Context3DVertexBufferFormat;
use namespace alternativa3d;
/**
* Types of attributes which defines format of vertex streams. It can be used as values of array,
* passed to <code>geometry.addVertexStream(attributes)</code> as an argument.
*
* @see alternativa.engine3d.resources.Geometry
*/
public class VertexAttributes {
/**
* Coordinates in 3D space. Defines by sequence of three floats.
*
* @see alternativa.engine3d.resources.Geometry
* @see #getAttributeStride()
*/
public static const POSITION:uint = 1;
/**
* Vertex normal. Defines by sequence of three floats.
*
* @see alternativa.engine3d.resources.Geometry
* @see #getAttributeStride()
*/
public static const NORMAL:uint = 2;
/**
* This data type combines values of vertex tangent and binormal within one sequence of four floats.
* The first three values defines tangent direction and the fourth can be 1 or -1 which defines to what side binormal is ordered.
*
* @see alternativa.engine3d.resources.Geometry
*/
public static const TANGENT4:uint = 3;
/**
* Data of linking of two <code>Joint</code>s with vertex. Defines by sequence of four floats in following order:
* id of the first <code>Joint</code> multiplied with 3, power of influence of the first <code>Joint</code>,
* id of the second <code>Joint</code> multiplied with 3, power of influence of the second <code>Joint</code>.
* There are a four 'slots' for this data type, so influence of 8 <code>Joint</code>s can be described.
* @see alternativa.engine3d.resources.Geometry
* @see alternativa.engine3d.objects.Skin
*/
public static const JOINTS:Vector.<uint> = Vector.<uint>([4,5,6,7]);
/**
* Texture coordinates data type. There are a 8 independent channels. Coordinates defines by the couples (u, v).
*
* @see alternativa.engine3d.resources.Geometry
*/
public static const TEXCOORDS:Vector.<uint> = Vector.<uint>([8,9,10,11,12,13,14,15]);
/**
* @private
*/
alternativa3d static const FORMATS:Array = [
Context3DVertexBufferFormat.FLOAT_1, //NONE
Context3DVertexBufferFormat.FLOAT_3, //POSITION
Context3DVertexBufferFormat.FLOAT_3, //NORMAL
Context3DVertexBufferFormat.FLOAT_4, //TANGENT4
Context3DVertexBufferFormat.FLOAT_4, //JOINTS[0]
Context3DVertexBufferFormat.FLOAT_4, //JOINTS[1]
Context3DVertexBufferFormat.FLOAT_4, //JOINTS[2]
Context3DVertexBufferFormat.FLOAT_4, //JOINTS[3]
Context3DVertexBufferFormat.FLOAT_2, //TEXCOORDS[0]
Context3DVertexBufferFormat.FLOAT_2, //TEXCOORDS[1]
Context3DVertexBufferFormat.FLOAT_2, //TEXCOORDS[2]
Context3DVertexBufferFormat.FLOAT_2, //TEXCOORDS[3]
Context3DVertexBufferFormat.FLOAT_2, //TEXCOORDS[4]
Context3DVertexBufferFormat.FLOAT_2, //TEXCOORDS[5]
Context3DVertexBufferFormat.FLOAT_2, //TEXCOORDS[6]
Context3DVertexBufferFormat.FLOAT_2 //TEXCOORDS[7]
];
/**
* Returns a dimensions of given attribute type (Number of floats by which defines given type)
*
* @param attribute Type of the attribute.
* @return
*/
public static function getAttributeStride(attribute:int):int {
switch(FORMATS[attribute]) {
case Context3DVertexBufferFormat.FLOAT_1:
return 1;
break;
case Context3DVertexBufferFormat.FLOAT_2:
return 2;
break;
case Context3DVertexBufferFormat.FLOAT_3:
return 3;
break;
case Context3DVertexBufferFormat.FLOAT_4:
return 4;
break;
}
return 0;
}
}
}

View File

@@ -0,0 +1,24 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.core {
import flash.display3D.VertexBuffer3D;
import flash.utils.ByteArray;
/**
* @private
*/
public class VertexStream {
public var buffer:VertexBuffer3D;
public var attributes:Array;
public var data:ByteArray;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,140 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.core.events {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.*;
import flash.events.Event;
use namespace alternativa3d;
public class Event3D extends Event {
/**
* Defines the value of the <code>type</code> property of a <code>added</code> event object.
* @eventType added
*/
public static const ADDED:String = "added3D";
/**
* Defines the value of the <code>type</code> property of a <code>removed</code> event object.
* @eventType removed
*/
public static const REMOVED:String = "removed3D";
/**
* This class should be used as base class for all events, which can have <code>Object3D</code> as an event target.
* @param type
* @param bubbles
*/
public function Event3D(type:String, bubbles:Boolean = true) {
super(type, bubbles);
_bubbles = bubbles;
}
/**
* @private
*/
alternativa3d var _target:Object3D;
/**
* @private
*/
alternativa3d var _currentTarget:Object3D;
/**
* @private
*/
alternativa3d var _bubbles:Boolean;
/**
* @private
*/
alternativa3d var _eventPhase:uint = 3;
/**
* @private
*/
alternativa3d var stop:Boolean = false;
/**
* @private
*/
alternativa3d var stopImmediate:Boolean = false;
/**
* Indicates whether an event is a bubbling event. If the event can bubble, this value is <code>true</code>; otherwise it is <code>false</code>.
*/
override public function get bubbles():Boolean {
return _bubbles;
}
/**
* The current phase in the event flow.
*/
override public function get eventPhase():uint {
return _eventPhase;
}
/**
* The event target. This property contains the target node.
*/
override public function get target():Object {
return _target;
}
/**
* The object that is actively processing the Event object with an event listener.
*/
override public function get currentTarget():Object {
return _currentTarget;
}
/**
* Prevents processing of any event listeners in nodes subsequent to the current node in the event flow.
* Does not affect on receiving events in listeners of (<code>currentTarget</code>).
*/
override public function stopPropagation():void {
stop = true;
}
/**
* Prevents processing of any event listeners in the current node and any subsequent nodes in the event flow.
*/
override public function stopImmediatePropagation():void {
stopImmediate = true;
}
/**
* Duplicates an instance of an Event subclass.
* Returns a new <code>Event3D</code> object that is a copy of the original instance of the <code>Event</code> object.
* @return A new <code>Event3D</code> object that is identical to the original.
*/
override public function clone():Event {
var result:Event3D = new Event3D(type, _bubbles);
result._target = _target;
result._currentTarget = _currentTarget;
result._eventPhase = _eventPhase;
return result;
}
/**
* Returns a string containing all the properties of the <code>Event3D</code> object.
* @return A string containing all the properties of the <code>Event3D</code> object
*/
override public function toString():String {
return formatToString("Event3D", "type", "bubbles", "eventPhase");
}
}
}

View File

@@ -0,0 +1,187 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.core.events {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.*;
import alternativa.engine3d.objects.Surface;
import flash.events.Event;
use namespace alternativa3d;
/**
*
* Event <code>MouseEvent3D</code> dispatches by <code>Object3D</code>, in cases when <code>MouseEvent</code> dispatches by <code>DisplayObject</code>.
*/
public class MouseEvent3D extends Event3D {
/**
* Defines the value of the <code>type</code> property of a <code>click3D</code> event object.
* @eventType click3D
*/
public static const CLICK:String = "click3D";
/**
* Defines the value of the <code>type</code> property of a <code>doubleClick3D</code> event object.
* @eventType doubleClick3D
*/
public static const DOUBLE_CLICK:String = "doubleClick3D";
/**
* Defines the value of the <code>type</code> property of a <code>mouseDown3D</code> event object.
* @eventType mouseDown3D
*/
public static const MOUSE_DOWN:String = "mouseDown3D";
/**
* Defines the value of the <code>type</code> property of a <code>mouseUp3D</code> event object.
* @eventType mouseUp3D
*/
public static const MOUSE_UP:String = "mouseUp3D";
/**
* Defines the value of the <code>type</code> property of a <code>mouseOver3D</code> event object.
* @eventType mouseOver3D
*/
public static const MOUSE_OVER:String = "mouseOver3D";
/**
* Defines the value of the <code>type</code> property of a <code>mouseOut3D</code> event object.
* @eventType mouseOut3D
*/
public static const MOUSE_OUT:String = "mouseOut3D";
/**
* Defines the value of the <code>type</code> property of a <code>rollOver3D</code> event object.
* @eventType rollOver3D
*/
public static const ROLL_OVER:String = "rollOver3D";
/**
* Defines the value of the <code>type</code> property of a <code>rollOut3D</code> event object.
* @eventType rollOut3D
*/
public static const ROLL_OUT:String = "rollOut3D";
/**
* Defines the value of the <code>type</code> property of a <code>mouseMove3D</code> event object.
* @eventType mouseMove3D
*/
public static const MOUSE_MOVE:String = "mouseMove3D";
/**
* Defines the value of the <code>type</code> property of a <code>mouseWheel3D</code> event object.
* @eventType mouseWheel3D
*/
public static const MOUSE_WHEEL:String = "mouseWheel3D";
/**
* On Windows or Linux, indicates whether the Ctrl key is active (<code>true</code>) or inactive (<code>false</code>). On Macintosh, indicates whether either the Control key or the Command key is activated.
*/
public var ctrlKey:Boolean;
/**
* Indicates whether the Alt key is active (<code>true</code>) or inactive (<code>false</code>).
*/
public var altKey:Boolean;
/**
* Indicates whether the Shift key is active (<code>true</code>) or inactive (<code>false</code>).
*/
public var shiftKey:Boolean;
/**
* Indicates whether the main mouse button is active (<code>true</code>) or inactive (<code>false</code>).
*/
public var buttonDown:Boolean;
/**
* Indicates how many lines should be scrolled for each unit the user rotates the mouse wheel.
*/
public var delta:int;
/**
* A reference to an object that is related to the event. This property applies to the <code>mouseOut</code>, <code>mouseOver</code>, <code>rollOut</code> и <code>rollOver</code>.
* For example, when <code>mouseOut</code> occurs, <code>relatedObject</code> point to the object over which mouse cursor placed now.
*/
public var relatedObject:Object3D;
/**
* X coordinate of the event at local target object's space.
*/
public var localX:Number;
/**
* Y coordinate of the event at local target object's space.
*/
public var localY:Number;
/**
* Z coordinate of the event at local target object's space.
*/
public var localZ:Number;
/**
* @private
*/
alternativa3d var _surface:Surface;
/**
* Creates a MouseEvent3D object.
* @param type Type.
* @param bubbles Indicates whether an event is a bubbling event.
* @param localY Y coordinate of the event at local target object's space.
* @param localX X coordinate of the event at local target object's space.
* @param localZ Z coordinate of the event at local target object's space.
* @param relatedObject <code>Object3D</code>, eelated to the <code>MouseEvent3D</code>.
* @param altKey Indicates whether the Alt key is active.
* @param ctrlKey Indicates whether the Control key is active.
* @param shiftKey Indicates whether the Shift key is active.
* @param buttonDown Indicates whether the main mouse button is active .
* @param delta Indicates how many lines should be scrolled for each unit the user rotates the mouse wheel.
*/
public function MouseEvent3D(type:String, bubbles:Boolean = true, localX:Number = NaN, localY:Number = NaN, localZ:Number = NaN, relatedObject:Object3D = null, ctrlKey:Boolean = false, altKey:Boolean = false, shiftKey:Boolean = false, buttonDown:Boolean = false, delta:int = 0) {
super(type, bubbles);
this.localX = localX;
this.localY = localY;
this.localZ = localZ;
this.relatedObject = relatedObject;
this.ctrlKey = ctrlKey;
this.altKey = altKey;
this.shiftKey = shiftKey;
this.buttonDown = buttonDown;
this.delta = delta;
}
/**
* <code>Surface</code> on which event has been received. The object that owns the surface, can differs from the target event.
*
*/
public function get surface():Surface {
return _surface;
}
/**
* Duplicates an instance of an Event subclass.
* Returns a new <code>MouseEvent3D</code> object that is a copy of the original instance of the Event object.
* @return A new <code>MouseEvent3D</code> object that is identical to the original.
*/
override public function clone():Event {
return new MouseEvent3D(type, _bubbles, localX, localY, localZ, relatedObject, ctrlKey, altKey, shiftKey, buttonDown, delta);
}
/**
* Returns a string containing all the properties of the <code>MouseEvent3D</code> object.
* @return A string containing all the properties of the <code>MouseEvent3D</code> object
*/
override public function toString():String {
return formatToString("MouseEvent3D", "type", "bubbles", "eventPhase", "localX", "localY", "localZ", "relatedObject", "altKey", "ctrlKey", "shiftKey", "buttonDown", "delta");
}
}
}

View File

@@ -0,0 +1,724 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.effects {
// ===========================================================================
// Imports
// ---------------------------------------------------------------------------
//import flash.display3D.*;
import flash.utils.*;
/**
* @private
*/
public class AGALMiniAssembler
{
// ======================================================================
// Properties
// ----------------------------------------------------------------------
// AGAL bytes and error buffer
private var _agalcode:ByteArray = null;
private var _error:String = "";
private var debugEnabled:Boolean = false;
private static var initialized:Boolean = false;
// ======================================================================
// Getters
// ----------------------------------------------------------------------
public function get error():String { return _error; }
public function get agalcode():ByteArray { return _agalcode; }
// ======================================================================
// Constructor
// ----------------------------------------------------------------------
public function AGALMiniAssembler( debugging:Boolean = false ):void
{
debugEnabled = debugging;
if ( !initialized )
init();
}
// ======================================================================
// Methods
// ----------------------------------------------------------------------
public function assemble( mode:String, source:String, verbose:Boolean = false ):ByteArray
{
var start:uint = getTimer();
_agalcode = new ByteArray();
_error = "";
var isFrag:Boolean = false;
if ( mode == FRAGMENT )
isFrag = true
else if ( mode != VERTEX )
_error = 'ERROR: mode needs to be "' + FRAGMENT + '" or "' + VERTEX + '" but is "' + mode + '".';
agalcode.endian = Endian.LITTLE_ENDIAN;
agalcode.writeByte( 0xa0 ); // tag version
agalcode.writeUnsignedInt( 0x1 ); // AGAL version, big endian, bit pattern will be 0x01000000
agalcode.writeByte( 0xa1 ); // tag program id
agalcode.writeByte( isFrag ? 1 : 0 ); // vertex or fragment
var lines:Array = source.replace( /[\f\n\r\v]+/g, "\n" ).split( "\n" );
var nest:int = 0;
var nops:int = 0;
var i:int;
var lng:int = lines.length;
for ( i = 0; i < lng && _error == ""; i++ )
{
var line:String = new String( lines[i] );
// remove comments
var startcomment:int = line.search( "//" );
if ( startcomment != -1 )
line = line.slice( 0, startcomment );
// grab options
var optsi:int = line.search( /<.*>/g );
var opts:Array;
if ( optsi != -1 )
{
opts = line.slice( optsi ).match( /([\w\.\-\+]+)/gi );
line = line.slice( 0, optsi );
}
// find opcode
var opCode:Array = line.match( /^\w{3}/ig );
var opFound:OpCode = OPMAP[ opCode[0] ];
// if debug is enabled, output the opcodes
if ( debugEnabled )
trace( opFound );
if ( opFound == null )
{
if ( line.length >= 3 )
trace( "warning: bad line "+i+": "+lines[i] );
continue;
}
line = line.slice( line.search( opFound.name ) + opFound.name.length );
// nesting check
if ( opFound.flags & OP_DEC_NEST )
{
nest--;
if ( nest < 0 )
{
_error = "error: conditional closes without open.";
break;
}
}
if ( opFound.flags & OP_INC_NEST )
{
nest++;
if ( nest > MAX_NESTING )
{
_error = "error: nesting to deep, maximum allowed is "+MAX_NESTING+".";
break;
}
}
if ( ( opFound.flags & OP_FRAG_ONLY ) && !isFrag )
{
_error = "error: opcode is only allowed in fragment programs.";
break;
}
if ( verbose )
trace( "emit opcode=" + opFound );
agalcode.writeUnsignedInt( opFound.emitCode );
nops++;
if ( nops > MAX_OPCODES )
{
_error = "error: too many opcodes. maximum is "+MAX_OPCODES+".";
break;
}
// get operands, use regexp
var regs:Array = line.match( /vc\[([vof][actps]?)(\d*)?(\.[xyzw](\+\d{1,3})?)?\](\.[xyzw]{1,4})?|([vof][actps]?)(\d*)?(\.[xyzw]{1,4})?/gi );
if ( regs.length != opFound.numRegister )
{
_error = "error: wrong number of operands. found "+regs.length+" but expected "+opFound.numRegister+".";
break;
}
var badreg:Boolean = false;
var pad:uint = 64 + 64 + 32;
var regLength:uint = regs.length;
for ( var j:int = 0; j < regLength; j++ )
{
var isRelative:Boolean = false;
var relreg:Array = regs[ j ].match( /\[.*\]/ig );
if ( relreg.length > 0 )
{
regs[ j ] = regs[ j ].replace( relreg[ 0 ], "0" );
if ( verbose )
trace( "IS REL" );
isRelative = true;
}
var res:Array = regs[j].match( /^\b[A-Za-z]{1,2}/ig );
var regFound:Register = REGMAP[ res[ 0 ] ];
// if debug is enabled, output the registers
if ( debugEnabled )
trace( regFound );
if ( regFound == null )
{
_error = "error: could not parse operand "+j+" ("+regs[j]+").";
badreg = true;
break;
}
if ( isFrag )
{
if ( !( regFound.flags & REG_FRAG ) )
{
_error = "error: register operand "+j+" ("+regs[j]+") only allowed in vertex programs.";
badreg = true;
break;
}
if ( isRelative )
{
_error = "error: register operand "+j+" ("+regs[j]+") relative adressing not allowed in fragment programs.";
badreg = true;
break;
}
}
else
{
if ( !( regFound.flags & REG_VERT ) )
{
_error = "error: register operand "+j+" ("+regs[j]+") only allowed in fragment programs.";
badreg = true;
break;
}
}
regs[j] = regs[j].slice( regs[j].search( regFound.name ) + regFound.name.length );
//trace( "REGNUM: " +regs[j] );
var idxmatch:Array = isRelative ? relreg[0].match( /\d+/ ) : regs[j].match( /\d+/ );
var regidx:uint = 0;
if ( idxmatch )
regidx = uint( idxmatch[0] );
if ( regFound.range < regidx )
{
_error = "error: register operand "+j+" ("+regs[j]+") index exceeds limit of "+(regFound.range+1)+".";
badreg = true;
break;
}
var regmask:uint = 0;
var maskmatch:Array = regs[j].match( /(\.[xyzw]{1,4})/ );
var isDest:Boolean = ( j == 0 && !( opFound.flags & OP_NO_DEST ) );
var isSampler:Boolean = ( j == 2 && ( opFound.flags & OP_SPECIAL_TEX ) );
var reltype:uint = 0;
var relsel:uint = 0;
var reloffset:int = 0;
if ( isDest && isRelative )
{
_error = "error: relative can not be destination";
badreg = true;
break;
}
if ( maskmatch )
{
regmask = 0;
var cv:uint;
var maskLength:uint = maskmatch[0].length;
for ( var k:int = 1; k < maskLength; k++ )
{
cv = maskmatch[0].charCodeAt(k) - "x".charCodeAt(0);
if ( cv > 2 )
cv = 3;
if ( isDest )
regmask |= 1 << cv;
else
regmask |= cv << ( ( k - 1 ) << 1 );
}
if ( !isDest )
for ( ; k <= 4; k++ )
regmask |= cv << ( ( k - 1 ) << 1 ) // repeat last
}
else
{
regmask = isDest ? 0xf : 0xe4; // id swizzle or mask
}
if ( isRelative )
{
var relname:Array = relreg[0].match( /[A-Za-z]{1,2}/ig );
var regFoundRel:Register = REGMAP[ relname[0]];
if ( regFoundRel == null )
{
_error = "error: bad index register";
badreg = true;
break;
}
reltype = regFoundRel.emitCode;
var selmatch:Array = relreg[0].match( /(\.[xyzw]{1,1})/ );
if ( selmatch.length==0 )
{
_error = "error: bad index register select";
badreg = true;
break;
}
relsel = selmatch[0].charCodeAt(1) - "x".charCodeAt(0);
if ( relsel > 2 )
relsel = 3;
var relofs:Array = relreg[0].match( /\+\d{1,3}/ig );
if ( relofs.length > 0 )
reloffset = relofs[0];
if ( reloffset < 0 || reloffset > 255 )
{
_error = "error: index offset "+reloffset+" out of bounds. [0..255]";
badreg = true;
break;
}
if ( verbose )
trace( "RELATIVE: type="+reltype+"=="+relname[0]+" sel="+relsel+"=="+selmatch[0]+" idx="+regidx+" offset="+reloffset );
}
if ( verbose )
trace( " emit argcode="+regFound+"["+regidx+"]["+regmask+"]" );
if ( isDest )
{
agalcode.writeShort( regidx );
agalcode.writeByte( regmask );
agalcode.writeByte( regFound.emitCode );
pad -= 32;
} else
{
if ( isSampler )
{
if ( verbose )
trace( " emit sampler" );
var samplerbits:uint = 5; // type 5
var optsLength:uint = opts.length;
var bias:Number = 0;
for ( k = 0; k<optsLength; k++ )
{
if ( verbose )
trace( " opt: "+opts[k] );
var optfound:Sampler = SAMPLEMAP [opts[k]];
if ( optfound == null )
{
// todo check that it's a number...
//trace( "Warning, unknown sampler option: "+opts[k] );
bias = Number(opts[k]);
if ( verbose )
trace( " bias: " + bias );
}
else
{
if ( optfound.flag != SAMPLER_SPECIAL_SHIFT )
samplerbits &= ~( 0xf << optfound.flag );
samplerbits |= uint( optfound.mask ) << uint( optfound.flag );
}
}
agalcode.writeShort( regidx );
agalcode.writeByte(int(bias*8.0));
agalcode.writeByte(0);
agalcode.writeUnsignedInt( samplerbits );
if ( verbose )
trace( " bits: " + ( samplerbits - 5 ) );
pad -= 64;
}
else
{
if ( j == 0 )
{
agalcode.writeUnsignedInt( 0 );
pad -= 32;
}
agalcode.writeShort( regidx );
agalcode.writeByte( reloffset );
agalcode.writeByte( regmask );
agalcode.writeByte( regFound.emitCode );
agalcode.writeByte( reltype );
agalcode.writeShort( isRelative ? ( relsel | ( 1 << 15 ) ) : 0 );
pad -= 64;
}
}
}
// pad unused regs
for ( j = 0; j < pad; j += 8 )
agalcode.writeByte( 0 );
if ( badreg )
break;
}
if ( _error != "" )
{
_error += "\n at line " + i + " " + lines[i];
agalcode.length = 0;
trace( _error );
}
// trace the bytecode bytes if debugging is enabled
if ( debugEnabled )
{
var dbgLine:String = "generated bytecode:";
var agalLength:uint = agalcode.length;
for ( var index:uint = 0; index < agalLength; index++ )
{
if ( !( index % 16 ) )
dbgLine += "\n";
if ( !( index % 4 ) )
dbgLine += " ";
var byteStr:String = agalcode[ index ].toString( 16 );
if ( byteStr.length < 2 )
byteStr = "0" + byteStr;
dbgLine += byteStr;
}
trace( dbgLine );
}
if ( verbose )
trace( "AGALMiniAssembler.assemble time: " + ( ( getTimer() - start ) / 1000 ) + "s" );
return agalcode;
}
static private function init():void
{
initialized = true;
// Fill the dictionaries with opcodes and registers
OPMAP[ MOV ] = new OpCode( MOV, 2, 0x00, 0 );
OPMAP[ ADD ] = new OpCode( ADD, 3, 0x01, 0 );
OPMAP[ SUB ] = new OpCode( SUB, 3, 0x02, 0 );
OPMAP[ MUL ] = new OpCode( MUL, 3, 0x03, 0 );
OPMAP[ DIV ] = new OpCode( DIV, 3, 0x04, 0 );
OPMAP[ RCP ] = new OpCode( RCP, 2, 0x05, 0 );
OPMAP[ MIN ] = new OpCode( MIN, 3, 0x06, 0 );
OPMAP[ MAX ] = new OpCode( MAX, 3, 0x07, 0 );
OPMAP[ FRC ] = new OpCode( FRC, 2, 0x08, 0 );
OPMAP[ SQT ] = new OpCode( SQT, 2, 0x09, 0 );
OPMAP[ RSQ ] = new OpCode( RSQ, 2, 0x0a, 0 );
OPMAP[ POW ] = new OpCode( POW, 3, 0x0b, 0 );
OPMAP[ LOG ] = new OpCode( LOG, 2, 0x0c, 0 );
OPMAP[ EXP ] = new OpCode( EXP, 2, 0x0d, 0 );
OPMAP[ NRM ] = new OpCode( NRM, 2, 0x0e, 0 );
OPMAP[ SIN ] = new OpCode( SIN, 2, 0x0f, 0 );
OPMAP[ COS ] = new OpCode( COS, 2, 0x10, 0 );
OPMAP[ CRS ] = new OpCode( CRS, 3, 0x11, 0 );
OPMAP[ DP3 ] = new OpCode( DP3, 3, 0x12, 0 );
OPMAP[ DP4 ] = new OpCode( DP4, 3, 0x13, 0 );
OPMAP[ ABS ] = new OpCode( ABS, 2, 0x14, 0 );
OPMAP[ NEG ] = new OpCode( NEG, 2, 0x15, 0 );
OPMAP[ SAT ] = new OpCode( SAT, 2, 0x16, 0 );
OPMAP[ M33 ] = new OpCode( M33, 3, 0x17, OP_SPECIAL_MATRIX );
OPMAP[ M44 ] = new OpCode( M44, 3, 0x18, OP_SPECIAL_MATRIX );
OPMAP[ M34 ] = new OpCode( M34, 3, 0x19, OP_SPECIAL_MATRIX );
OPMAP[ IFZ ] = new OpCode( IFZ, 1, 0x1a, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
OPMAP[ INZ ] = new OpCode( INZ, 1, 0x1b, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
OPMAP[ IFE ] = new OpCode( IFE, 2, 0x1c, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
OPMAP[ INE ] = new OpCode( INE, 2, 0x1d, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
OPMAP[ IFG ] = new OpCode( IFG, 2, 0x1e, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
OPMAP[ IFL ] = new OpCode( IFL, 2, 0x1f, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
OPMAP[ IEG ] = new OpCode( IEG, 2, 0x20, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
OPMAP[ IEL ] = new OpCode( IEL, 2, 0x21, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
OPMAP[ ELS ] = new OpCode( ELS, 0, 0x22, OP_NO_DEST | OP_INC_NEST | OP_DEC_NEST );
OPMAP[ EIF ] = new OpCode( EIF, 0, 0x23, OP_NO_DEST | OP_DEC_NEST );
OPMAP[ REP ] = new OpCode( REP, 1, 0x24, OP_NO_DEST | OP_INC_NEST | OP_SCALAR );
OPMAP[ ERP ] = new OpCode( ERP, 0, 0x25, OP_NO_DEST | OP_DEC_NEST );
OPMAP[ BRK ] = new OpCode( BRK, 0, 0x26, OP_NO_DEST );
OPMAP[ KIL ] = new OpCode( KIL, 1, 0x27, OP_NO_DEST | OP_FRAG_ONLY );
OPMAP[ TEX ] = new OpCode( TEX, 3, 0x28, OP_FRAG_ONLY | OP_SPECIAL_TEX );
OPMAP[ SGE ] = new OpCode( SGE, 3, 0x29, 0 );
OPMAP[ SLT ] = new OpCode( SLT, 3, 0x2a, 0 );
OPMAP[ SGN ] = new OpCode( SGN, 2, 0x2b, 0 );
REGMAP[ VA ] = new Register( VA, "vertex attribute", 0x0, 7, REG_VERT | REG_READ );
REGMAP[ VC ] = new Register( VC, "vertex constant", 0x1, 127, REG_VERT | REG_READ );
REGMAP[ VT ] = new Register( VT, "vertex temporary", 0x2, 7, REG_VERT | REG_WRITE | REG_READ );
REGMAP[ OP ] = new Register( OP, "vertex output", 0x3, 0, REG_VERT | REG_WRITE );
REGMAP[ V ] = new Register( V, "varying", 0x4, 7, REG_VERT | REG_FRAG | REG_READ | REG_WRITE );
REGMAP[ FC ] = new Register( FC, "fragment constant", 0x1, 27, REG_FRAG | REG_READ );
REGMAP[ FT ] = new Register( FT, "fragment temporary", 0x2, 7, REG_FRAG | REG_WRITE | REG_READ );
REGMAP[ FS ] = new Register( FS, "texture sampler", 0x5, 7, REG_FRAG | REG_READ );
REGMAP[ OC ] = new Register( OC, "fragment output", 0x3, 0, REG_FRAG | REG_WRITE );
SAMPLEMAP[ D2 ] = new Sampler( D2, SAMPLER_DIM_SHIFT, 0 );
SAMPLEMAP[ D3 ] = new Sampler( D3, SAMPLER_DIM_SHIFT, 2 );
SAMPLEMAP[ CUBE ] = new Sampler( CUBE, SAMPLER_DIM_SHIFT, 1 );
SAMPLEMAP[ MIPNEAREST ] = new Sampler( MIPNEAREST, SAMPLER_MIPMAP_SHIFT, 1 );
SAMPLEMAP[ MIPLINEAR ] = new Sampler( MIPLINEAR, SAMPLER_MIPMAP_SHIFT, 2 );
SAMPLEMAP[ MIPNONE ] = new Sampler( MIPNONE, SAMPLER_MIPMAP_SHIFT, 0 );
SAMPLEMAP[ NOMIP ] = new Sampler( NOMIP, SAMPLER_MIPMAP_SHIFT, 0 );
SAMPLEMAP[ NEAREST ] = new Sampler( NEAREST, SAMPLER_FILTER_SHIFT, 0 );
SAMPLEMAP[ LINEAR ] = new Sampler( LINEAR, SAMPLER_FILTER_SHIFT, 1 );
SAMPLEMAP[ CENTROID ] = new Sampler( CENTROID, SAMPLER_SPECIAL_SHIFT, 1 << 0 );
SAMPLEMAP[ SINGLE ] = new Sampler( SINGLE, SAMPLER_SPECIAL_SHIFT, 1 << 1 );
SAMPLEMAP[ DEPTH ] = new Sampler( DEPTH, SAMPLER_SPECIAL_SHIFT, 1 << 2 );
SAMPLEMAP[ REPEAT ] = new Sampler( REPEAT, SAMPLER_REPEAT_SHIFT, 1 );
SAMPLEMAP[ WRAP ] = new Sampler( WRAP, SAMPLER_REPEAT_SHIFT, 1 );
SAMPLEMAP[ CLAMP ] = new Sampler( CLAMP, SAMPLER_REPEAT_SHIFT, 0 );
}
// ======================================================================
// Constants
// ----------------------------------------------------------------------
private static const OPMAP:Dictionary = new Dictionary();
private static const REGMAP:Dictionary = new Dictionary();
private static const SAMPLEMAP:Dictionary = new Dictionary();
private static const MAX_NESTING:int = 4;
private static const MAX_OPCODES:int = 256;
private static const FRAGMENT:String = "fragment";
private static const VERTEX:String = "vertex";
// masks and shifts
private static const SAMPLER_DIM_SHIFT:uint = 12;
private static const SAMPLER_SPECIAL_SHIFT:uint = 16;
private static const SAMPLER_REPEAT_SHIFT:uint = 20;
private static const SAMPLER_MIPMAP_SHIFT:uint = 24;
private static const SAMPLER_FILTER_SHIFT:uint = 28;
// regmap flags
private static const REG_WRITE:uint = 0x1;
private static const REG_READ:uint = 0x2;
private static const REG_FRAG:uint = 0x20;
private static const REG_VERT:uint = 0x40;
// opmap flags
private static const OP_SCALAR:uint = 0x1;
private static const OP_INC_NEST:uint = 0x2;
private static const OP_DEC_NEST:uint = 0x4;
private static const OP_SPECIAL_TEX:uint = 0x8;
private static const OP_SPECIAL_MATRIX:uint = 0x10;
private static const OP_FRAG_ONLY:uint = 0x20;
private static const OP_VERT_ONLY:uint = 0x40;
private static const OP_NO_DEST:uint = 0x80;
// opcodes
private static const MOV:String = "mov";
private static const ADD:String = "add";
private static const SUB:String = "sub";
private static const MUL:String = "mul";
private static const DIV:String = "div";
private static const RCP:String = "rcp";
private static const MIN:String = "min";
private static const MAX:String = "max";
private static const FRC:String = "frc";
private static const SQT:String = "sqt";
private static const RSQ:String = "rsq";
private static const POW:String = "pow";
private static const LOG:String = "log";
private static const EXP:String = "exp";
private static const NRM:String = "nrm";
private static const SIN:String = "sin";
private static const COS:String = "cos";
private static const CRS:String = "crs";
private static const DP3:String = "dp3";
private static const DP4:String = "dp4";
private static const ABS:String = "abs";
private static const NEG:String = "neg";
private static const SAT:String = "sat";
private static const M33:String = "m33";
private static const M44:String = "m44";
private static const M34:String = "m34";
private static const IFZ:String = "ifz";
private static const INZ:String = "inz";
private static const IFE:String = "ife";
private static const INE:String = "ine";
private static const IFG:String = "ifg";
private static const IFL:String = "ifl";
private static const IEG:String = "ieg";
private static const IEL:String = "iel";
private static const ELS:String = "els";
private static const EIF:String = "eif";
private static const REP:String = "rep";
private static const ERP:String = "erp";
private static const BRK:String = "brk";
private static const KIL:String = "kil";
private static const TEX:String = "tex";
private static const SGE:String = "sge";
private static const SLT:String = "slt";
private static const SGN:String = "sgn";
// registers
private static const VA:String = "va";
private static const VC:String = "vc";
private static const VT:String = "vt";
private static const OP:String = "op";
private static const V:String = "v";
private static const FC:String = "fc";
private static const FT:String = "ft";
private static const FS:String = "fs";
private static const OC:String = "oc";
// samplers
private static const D2:String = "2d";
private static const D3:String = "3d";
private static const CUBE:String = "cube";
private static const MIPNEAREST:String = "mipnearest";
private static const MIPLINEAR:String = "miplinear";
private static const MIPNONE:String = "mipnone";
private static const NOMIP:String = "nomip";
private static const NEAREST:String = "nearest";
private static const LINEAR:String = "linear";
private static const CENTROID:String = "centroid";
private static const SINGLE:String = "single";
private static const DEPTH:String = "depth";
private static const REPEAT:String = "repeat";
private static const WRAP:String = "wrap";
private static const CLAMP:String = "clamp";
}
}
// ================================================================================
// Helper Classes
// --------------------------------------------------------------------------------
{
// ===========================================================================
// Class
// ---------------------------------------------------------------------------
class OpCode
{
// ======================================================================
// Properties
// ----------------------------------------------------------------------
private var _emitCode:uint;
private var _flags:uint;
private var _name:String;
private var _numRegister:uint;
// ======================================================================
// Getters
// ----------------------------------------------------------------------
public function get emitCode():uint { return _emitCode; }
public function get flags():uint { return _flags; }
public function get name():String { return _name; }
public function get numRegister():uint { return _numRegister; }
// ======================================================================
// Constructor
// ----------------------------------------------------------------------
public function OpCode( name:String, numRegister:uint, emitCode:uint, flags:uint)
{
_name = name;
_numRegister = numRegister;
_emitCode = emitCode;
_flags = flags;
}
// ======================================================================
// Methods
// ----------------------------------------------------------------------
public function toString():String
{
return "[OpCode name=\""+_name+"\", numRegister="+_numRegister+", emitCode="+_emitCode+", flags="+_flags+"]";
}
}
// ===========================================================================
// Class
// ---------------------------------------------------------------------------
class Register
{
// ======================================================================
// Properties
// ----------------------------------------------------------------------
private var _emitCode:uint;
private var _name:String;
private var _longName:String;
private var _flags:uint;
private var _range:uint;
// ======================================================================
// Getters
// ----------------------------------------------------------------------
public function get emitCode():uint { return _emitCode; }
public function get longName():String { return _longName; }
public function get name():String { return _name; }
public function get flags():uint { return _flags; }
public function get range():uint { return _range; }
// ======================================================================
// Constructor
// ----------------------------------------------------------------------
public function Register( name:String, longName:String, emitCode:uint, range:uint, flags:uint)
{
_name = name;
_longName = longName;
_emitCode = emitCode;
_range = range;
_flags = flags;
}
// ======================================================================
// Methods
// ----------------------------------------------------------------------
public function toString():String
{
return "[Register name=\""+_name+"\", longName=\""+_longName+"\", emitCode="+_emitCode+", range="+_range+", flags="+ _flags+"]";
}
}
// ===========================================================================
// Class
// ---------------------------------------------------------------------------
class Sampler
{
// ======================================================================
// Properties
// ----------------------------------------------------------------------
private var _flag:uint;
private var _mask:uint;
private var _name:String;
// ======================================================================
// Getters
// ----------------------------------------------------------------------
public function get flag():uint { return _flag; }
public function get mask():uint { return _mask; }
public function get name():String { return _name; }
// ======================================================================
// Constructor
// ----------------------------------------------------------------------
public function Sampler( name:String, flag:uint, mask:uint )
{
_name = name;
_flag = flag;
_mask = mask;
}
// ======================================================================
// Methods
// ----------------------------------------------------------------------
public function toString():String
{
return "[Sampler name=\""+_name+"\", flag=\""+_flag+"\", mask="+mask+"]";
}
}
}

View File

@@ -0,0 +1,63 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.effects {
import flash.display3D.textures.TextureBase;
/**
* @private
*/
public class Particle {
public var diffuse:TextureBase;
public var opacity:TextureBase;
public var blendSource:String;
public var blendDestination:String;
public var x:Number;
public var y:Number;
public var z:Number;
public var rotation:Number;
public var width:Number;
public var height:Number;
public var originX:Number;
public var originY:Number;
public var uvScaleX:Number;
public var uvScaleY:Number;
public var uvOffsetX:Number;
public var uvOffsetY:Number;
public var red:Number;
public var green:Number;
public var blue:Number;
public var alpha:Number;
public var next:Particle;
static public var collector:Particle;
static public function create():Particle {
var res:Particle;
if (collector != null) {
res = collector;
collector = collector.next;
res.next = null;
} else {
//trace("new Particle");
res = new Particle();
}
return res;
}
}
}

View File

@@ -0,0 +1,183 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.effects {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.BoundBox;
import alternativa.engine3d.core.Transform3D;
import flash.geom.Vector3D;
use namespace alternativa3d;
/**
* @private
*/
public class ParticleEffect {
public var name:String;
public var scale:Number = 1;
public var boundBox:BoundBox;
alternativa3d var next:ParticleEffect;
alternativa3d var nextInSystem:ParticleEffect;
alternativa3d var system:ParticleSystem;
alternativa3d var startTime:Number;
alternativa3d var lifeTime:Number = Number.MAX_VALUE;
alternativa3d var particleList:Particle;
alternativa3d var aabb:BoundBox = new BoundBox();
alternativa3d var keyPosition:Vector3D;
protected var keyDirection:Vector3D;
protected var timeKeys:Vector.<Number> = new Vector.<Number>();
protected var positionKeys:Vector.<Vector3D> = new Vector.<Vector3D>();
protected var directionKeys:Vector.<Vector3D> = new Vector.<Vector3D>();
protected var scriptKeys:Vector.<Function> = new Vector.<Function>();
protected var keysCount:int = 0;
private static var randomNumbers:Vector.<Number>;
private static const randomNumbersCount:int = 1000;
private static const vector:Vector3D = new Vector3D();
private var randomOffset:int;
private var randomCounter:int;
private var _position:Vector3D = new Vector3D(0, 0, 0);
private var _direction:Vector3D = new Vector3D(0, 0, 1);
public function ParticleEffect() {
if (randomNumbers == null) {
randomNumbers = new Vector.<Number>();
for (var i:int = 0; i < randomNumbersCount; i++) randomNumbers[i] = Math.random();
}
randomOffset = Math.random()*randomNumbersCount;
}
public function get position():Vector3D {
return _position.clone();
}
public function set position(value:Vector3D):void {
_position.x = value.x;
_position.y = value.y;
_position.z = value.z;
_position.w = value.w;
if (system != null) setPositionKeys(system.getTime() - startTime);
}
public function get direction():Vector3D {
return _direction.clone();
}
public function set direction(value:Vector3D):void {
_direction.x = value.x;
_direction.y = value.y;
_direction.z = value.z;
_direction.w = value.w;
if (system != null) setDirectionKeys(system.getTime() - startTime);
}
public function stop():void {
var time:Number = system.getTime() - startTime;
for (var i:int = 0; i < keysCount; i++) {
if (time < timeKeys[i]) break;
}
keysCount = i;
}
protected function get particleSystem():ParticleSystem {
return system;
}
protected function get cameraTransform():Transform3D {
return system.cameraToLocalTransform;
}
protected function random():Number {
var res:Number = randomNumbers[randomCounter]; randomCounter++;
if (randomCounter == randomNumbersCount) randomCounter = 0;
return res;
}
protected function addKey(time:Number, script:Function):void {
timeKeys[keysCount] = time;
positionKeys[keysCount] = new Vector3D();
directionKeys[keysCount] = new Vector3D();
scriptKeys[keysCount] = script;
keysCount++;
}
protected function setLife(time:Number):void {
lifeTime = time;
}
alternativa3d function calculateAABB():void {
aabb.minX = boundBox.minX*scale + _position.x;
aabb.minY = boundBox.minY*scale + _position.y;
aabb.minZ = boundBox.minZ*scale + _position.z;
aabb.maxX = boundBox.maxX*scale + _position.x;
aabb.maxY = boundBox.maxY*scale + _position.y;
aabb.maxZ = boundBox.maxZ*scale + _position.z;
}
alternativa3d function setPositionKeys(time:Number):void {
for (var i:int = 0; i < keysCount; i++) {
if (time <= timeKeys[i]) {
var pos:Vector3D = positionKeys[i];
pos.x = _position.x;
pos.y = _position.y;
pos.z = _position.z;
}
}
}
alternativa3d function setDirectionKeys(time:Number):void {
vector.x = _direction.x;
vector.y = _direction.y;
vector.z = _direction.z;
vector.normalize();
for (var i:int = 0; i < keysCount; i++) {
if (time <= timeKeys[i]) {
var dir:Vector3D = directionKeys[i];
dir.x = vector.x;
dir.y = vector.y;
dir.z = vector.z;
}
}
}
alternativa3d function calculate(time:Number):Boolean {
randomCounter = randomOffset;
for (var i:int = 0; i < keysCount; i++) {
var keyTime:Number = timeKeys[i];
if (time >= keyTime) {
keyPosition = positionKeys[i];
keyDirection = directionKeys[i];
var script:Function = scriptKeys[i];
script.call(this, keyTime, time - keyTime);
} else break;
}
return i < keysCount || particleList != null;
}
}
}

View File

@@ -0,0 +1,139 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.effects {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Transform3D;
import flash.geom.Vector3D;
use namespace alternativa3d;
/**
* @private
*/
public class ParticlePrototype {
// Atlas
public var atlas:TextureAtlas;
// Blend
private var blendSource:String;
private var blendDestination:String;
// If <code>true</code>, then play animation
private var animated:Boolean;
// Size
private var width:Number;
private var height:Number;
// Key frames of animation.
private var timeKeys:Vector.<Number> = new Vector.<Number>();
private var rotationKeys:Vector.<Number> = new Vector.<Number>();
private var scaleXKeys:Vector.<Number> = new Vector.<Number>();
private var scaleYKeys:Vector.<Number> = new Vector.<Number>();
private var redKeys:Vector.<Number> = new Vector.<Number>();
private var greenKeys:Vector.<Number> = new Vector.<Number>();
private var blueKeys:Vector.<Number> = new Vector.<Number>();
private var alphaKeys:Vector.<Number> = new Vector.<Number>();
private var keysCount:int = 0;
public function ParticlePrototype(width:Number, height:Number, atlas:TextureAtlas, animated:Boolean = false, blendSource:String = "sourceAlpha", blendDestination:String = "oneMinusSourceAlpha") {
this.width = width;
this.height = height;
this.atlas = atlas;
this.animated = animated;
this.blendSource = blendSource;
this.blendDestination = blendDestination;
}
public function addKey(time:Number, rotation:Number = 0, scaleX:Number = 1, scaleY:Number = 1, red:Number = 1, green:Number = 1, blue:Number = 1, alpha:Number = 1):void {
var lastIndex:int = keysCount - 1;
if (keysCount > 0 && time <= timeKeys[lastIndex]) throw new Error("Keys must be successively.");
timeKeys[keysCount] = time;
rotationKeys[keysCount] = rotation;
scaleXKeys[keysCount] = scaleX;
scaleYKeys[keysCount] = scaleY;
redKeys[keysCount] = red;
greenKeys[keysCount] = green;
blueKeys[keysCount] = blue;
alphaKeys[keysCount] = alpha;
keysCount++;
}
public function createParticle(effect:ParticleEffect, time:Number, position:Vector3D, rotation:Number = 0, scaleX:Number = 1, scaleY:Number = 1, alpha:Number = 1, firstFrame:int = 0):void {
var b:int = keysCount - 1;
if (atlas.diffuse._texture != null && keysCount > 1 && time >= timeKeys[0] && time < timeKeys[b]) {
for (b = 1; b < keysCount; b++) {
if (time < timeKeys[b]) {
var systemScale:Number = effect.system.scale;
var effectScale:Number = effect.scale;
var transform:Transform3D = effect.system.localToCameraTransform;
var wind:Vector3D = effect.system.wind;
var gravity:Vector3D = effect.system.gravity;
// Interpolation
var a:int = b - 1;
var t:Number = (time - timeKeys[a])/(timeKeys[b] - timeKeys[a]);
// Frame calculation
var pos:int = firstFrame + (animated ? time*atlas.fps : 0);
if (atlas.loop) {
pos = pos%atlas.rangeLength;
if (pos < 0) pos += atlas.rangeLength;
} else {
if (pos < 0) pos = 0;
if (pos >= atlas.rangeLength) pos = atlas.rangeLength - 1;
}
pos += atlas.rangeBegin;
var col:int = pos%atlas.columnsCount;
var row:int = pos/atlas.columnsCount;
// Particle creation
var particle:Particle = Particle.create();
particle.diffuse = atlas.diffuse._texture;
particle.opacity = (atlas.opacity != null) ? atlas.opacity._texture : null;
particle.blendSource = blendSource;
particle.blendDestination = blendDestination;
var cx:Number = effect.keyPosition.x + position.x*effectScale;
var cy:Number = effect.keyPosition.y + position.y*effectScale;
var cz:Number = effect.keyPosition.z + position.z*effectScale;
particle.x = cx*transform.a + cy*transform.b + cz*transform.c + transform.d;
particle.y = cx*transform.e + cy*transform.f + cz*transform.g + transform.h;
particle.z = cx*transform.i + cy*transform.j + cz*transform.k + transform.l;
var rot:Number = rotationKeys[a] + (rotationKeys[b] - rotationKeys[a])*t;
particle.rotation = (scaleX*scaleY > 0) ? (rotation + rot) : (rotation - rot);
particle.width = systemScale*effectScale*scaleX*width*(scaleXKeys[a] + (scaleXKeys[b] - scaleXKeys[a])*t);
particle.height = systemScale*effectScale*scaleY*height*(scaleYKeys[a] + (scaleYKeys[b] - scaleYKeys[a])*t);
particle.originX = atlas.originX;
particle.originY = atlas.originY;
particle.uvScaleX = 1/atlas.columnsCount;
particle.uvScaleY = 1/atlas.rowsCount;
particle.uvOffsetX = col/atlas.columnsCount;
particle.uvOffsetY = row/atlas.rowsCount;
particle.red = redKeys[a] + (redKeys[b] - redKeys[a])*t;
particle.green = greenKeys[a] + (greenKeys[b] - greenKeys[a])*t;
particle.blue = blueKeys[a] + (blueKeys[b] - blueKeys[a])*t;
particle.alpha = alpha*(alphaKeys[a] + (alphaKeys[b] - alphaKeys[a])*t);
particle.next = effect.particleList;
effect.particleList = particle;
break;
}
}
}
}
public function get lifeTime():Number {
var lastIndex:int = keysCount - 1;
return timeKeys[lastIndex];
}
}
}

View File

@@ -0,0 +1,481 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.effects {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Debug;
import alternativa.engine3d.core.DrawUnit;
import alternativa.engine3d.core.Light3D;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Renderer;
import alternativa.engine3d.materials.compiler.Procedure;
import flash.display3D.Context3D;
import flash.display3D.Context3DBlendFactor;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DTriangleFace;
import flash.display3D.Context3DVertexBufferFormat;
import flash.display3D.IndexBuffer3D;
import flash.display3D.Program3D;
import flash.display3D.VertexBuffer3D;
import flash.display3D.textures.TextureBase;
import flash.geom.Vector3D;
import flash.utils.ByteArray;
import flash.utils.getTimer;
use namespace alternativa3d;
/**
* @private
*/
public class ParticleSystem extends Object3D {
static private const limit:int = 31;
static private var vertexBuffer:VertexBuffer3D;
static private var indexBuffer:IndexBuffer3D;
static private var diffuseProgram:Program3D;
static private var opacityProgram:Program3D;
static private var diffuseBlendProgram:Program3D;
static private var opacityBlendProgram:Program3D;
public var resolveByAABB:Boolean = true;
public var gravity:Vector3D = new Vector3D(0, 0, -1);
public var wind:Vector3D = new Vector3D();
public var fogColor:int = 0;
public var fogMaxDensity:Number = 0;
public var fogNear:Number = 0;
public var fogFar:Number = 0;
alternativa3d var scale:Number = 1;
alternativa3d var effectList:ParticleEffect;
private var drawUnit:DrawUnit = null;
private var diffuse:TextureBase = null;
private var opacity:TextureBase = null;
private var blendSource:String = null;
private var blendDestination:String = null;
private var counter:int;
private var za:Number;
private var zb:Number;
private var fake:Vector.<Object3D> = new Vector.<Object3D>();
private var fakeCounter:int = 0;
public function ParticleSystem() {
super();
}
private var pause:Boolean = false;
private var stopTime:Number;
private var subtractiveTime:Number = 0;
public function stop():void {
if (!pause) {
stopTime = getTimer()*0.001;
pause = true;
}
}
public function play():void {
if (pause) {
subtractiveTime += getTimer()*0.001 - stopTime;
pause = false;
}
}
public function prevFrame():void {
stopTime -= 0.001;
}
public function nextFrame():void {
stopTime += 0.001;
}
public function addEffect(effect:ParticleEffect):ParticleEffect {
// Checking on belonging
if (effect.system != null) throw new Error("Cannot add the same effect twice.");
// Set parameters
effect.startTime = getTime();
effect.system = this;
effect.setPositionKeys(0);
effect.setDirectionKeys(0);
// Add
effect.nextInSystem = effectList;
effectList = effect;
return effect;
}
public function getEffectByName(name:String):ParticleEffect {
for (var effect:ParticleEffect = effectList; effect != null; effect = effect.nextInSystem) {
if (effect.name == name) return effect;
}
return null;
}
alternativa3d function getTime():Number {
return pause ? (stopTime - subtractiveTime) : (getTimer()*0.001 - subtractiveTime);
}
override alternativa3d function collectDraws(camera:Camera3D, lights:Vector.<Light3D>, lightsLength:int):void {
// Create geometry and program
if (vertexBuffer == null) createAndUpload(camera.context3D);
// Average size
scale = Math.sqrt(localToCameraTransform.a*localToCameraTransform.a + localToCameraTransform.e*localToCameraTransform.e + localToCameraTransform.i*localToCameraTransform.i);
scale += Math.sqrt(localToCameraTransform.b*localToCameraTransform.b + localToCameraTransform.f*localToCameraTransform.f + localToCameraTransform.j*localToCameraTransform.j);
scale += Math.sqrt(localToCameraTransform.c*localToCameraTransform.c + localToCameraTransform.g*localToCameraTransform.g + localToCameraTransform.k*localToCameraTransform.k);
scale /= 3;
// TODO: add rotation on slope of the Z-axis in local space of camera
// Calculate frustrum
camera.calculateFrustum(cameraToLocalTransform);
// Loop items
var visibleEffectList:ParticleEffect;
var conflictAnyway:Boolean = false;
var time:Number = getTime();
for (var effect:ParticleEffect = effectList, prev:ParticleEffect = null; effect != null;) {
// Check if actual
var effectTime:Number = time - effect.startTime;
if (effectTime <= effect.lifeTime) {
// Check bounds
var culling:int = 63;
if (effect.boundBox != null) {
effect.calculateAABB();
culling = effect.aabb.checkFrustumCulling(camera.frustum, 63);
}
if (culling >= 0) {
// Gather the particles
if (effect.calculate(effectTime)) {
// Add
if (effect.particleList != null) {
effect.next = visibleEffectList;
visibleEffectList = effect;
conflictAnyway ||= effect.boundBox == null;
}
// Go to next effect
prev = effect;
effect = effect.nextInSystem;
} else {
// Removing
if (prev != null) {
prev.nextInSystem = effect.nextInSystem;
effect = prev.nextInSystem;
} else {
effectList = effect.nextInSystem;
effect = effectList;
}
}
} else {
// Go to next effect
prev = effect;
effect = effect.nextInSystem;
}
} else {
// Removing
if (prev != null) {
prev.nextInSystem = effect.nextInSystem;
effect = prev.nextInSystem;
} else {
effectList = effect.nextInSystem;
effect = effectList;
}
}
}
// Gather draws
if (visibleEffectList != null) {
if (visibleEffectList.next != null) {
/*if (resolveByAABB && !conflictAnyway) {
drawAABBEffects(camera, visibleEffectList);
} else {*/
drawConflictEffects(camera, visibleEffectList);
//}
} else {
drawParticleList(camera, visibleEffectList.particleList);
visibleEffectList.particleList = null;
if (camera.debug && visibleEffectList.boundBox != null && (camera.checkInDebug(this) & Debug.BOUNDS)) Debug.drawBoundBox(camera, visibleEffectList.aabb, localToCameraTransform);
}
// Reset
flush(camera);
drawUnit = null;
diffuse = null;
opacity = null;
blendSource = null;
blendDestination = null;
fakeCounter = 0;
}
}
private function createAndUpload(context:Context3D):void {
var vertices:Vector.<Number> = new Vector.<Number>();
var indices:Vector.<uint> = new Vector.<uint>();
for (var i:int = 0; i < limit; i++) {
vertices.push(0,0,0, 0,0,i*4, 0,1,0, 0,1,i*4, 1,1,0, 1,1,i*4, 1,0,0, 1,0,i*4);
indices.push(i*4, i*4 + 1, i*4 + 3, i*4 + 2, i*4 + 3, i*4 + 1);
}
vertexBuffer = context.createVertexBuffer(limit*4, 6);
vertexBuffer.uploadFromVector(vertices, 0, limit*4);
indexBuffer = context.createIndexBuffer(limit*6);
indexBuffer.uploadFromVector(indices, 0, limit*6);
var vertexProgram:Array = [
// Pivot
"mov t2, c[a1.z]", // originX, originY, width, height
"sub t0.z, a0.x, t2.x",
"sub t0.w, a0.y, t2.y",
// Width and height
"mul t0.z, t0.z, t2.z",
"mul t0.w, t0.w, t2.w",
// Rotation
"mov t2, c[a1.z+1]", // x, y, z, rotation
"mov t1.z, t2.w",
"sin t1.x, t1.z", // sin
"cos t1.y, t1.z", // cos
"mul t1.z, t0.z, t1.y", // x*cos
"mul t1.w, t0.w, t1.x", // y*sin
"sub t0.x, t1.z, t1.w", // X
"mul t1.z, t0.z, t1.x", // x*sin
"mul t1.w, t0.w, t1.y", // y*cos
"add t0.y, t1.z, t1.w", // Y
// Translation
"add t0.x, t0.x, t2.x",
"add t0.y, t0.y, t2.y",
"add t0.z, a0.z, t2.z",
"mov t0.w, a0.w",
// Projection
"dp4 o0.x, t0, c124",
"dp4 o0.y, t0, c125",
"dp4 o0.z, t0, c126",
"dp4 o0.w, t0, c127",
// UV correction and passing out
"mov t2, c[a1.z+2]", // uvScaleX, uvScaleY, uvOffsetX, uvOffsetY
"mul t1.x, a1.x, t2.x",
"mul t1.y, a1.y, t2.y",
"add t1.x, t1.x, t2.z",
"add t1.y, t1.y, t2.w",
"mov v0, t1",
// Passing color
"mov v1, c[a1.z+3]", // red, green, blue, alpha
// Passing coordinates in the camera space
"mov v2, t0",
];
var fragmentDiffuseProgram:Array = [
"tex t0, v0, s0 <2d,clamp,linear,miplinear>",
"mul t0, t0, v1",
// Fog
"sub t1.w, v2.z, c1.x",
"div t1.w, t1.w, c1.y",
"max t1.w, t1.w, c1.z",
"min t1.w, t1.w, c0.w",
"sub t1.xyz, c0.xyz, t0.xyz",
"mul t1.xyz, t1.xyz, t1.w",
"add t0.xyz, t0.xyz, t1.xyz",
"mov o0, t0",
];
var fragmentOpacityProgram:Array = [
"tex t0, v0, s0 <2d,clamp,linear,miplinear>",
"tex t1, v0, s1 <2d,clamp,linear,miplinear>",
"mov t0.w, t1.x",
"mul t0, t0, v1",
// Fog
"sub t1.w, v2.z, c1.x",
"div t1.w, t1.w, c1.y",
"max t1.w, t1.w, c1.z",
"min t1.w, t1.w, c0.w",
"sub t1.xyz, c0.xyz, t0.xyz",
"mul t1.xyz, t1.xyz, t1.w",
"add t0.xyz, t0.xyz, t1.xyz",
"mov o0, t0",
];
var fragmentDiffuseBlendProgram:Array = [
"tex t0, v0, s0 <2d,clamp,linear,miplinear>",
"mul t0, t0, v1",
// Fog
"sub t1.w, v2.z, c1.x",
"div t1.w, t1.w, c1.y",
"max t1.w, t1.w, c1.z",
"min t1.w, t1.w, c0.w",
"sub t1.w, c1.w, t1.w",
"mul t0.w, t0.w, t1.w",
"mov o0, t0",
];
var fragmentOpacityBlendProgram:Array = [
"tex t0, v0, s0 <2d,clamp,linear,miplinear>",
"tex t1, v0, s1 <2d,clamp,linear,miplinear>",
"mov t0.w, t1.x",
"mul t0, t0, v1",
// Fog
"sub t1.w, v2.z, c1.x",
"div t1.w, t1.w, c1.y",
"max t1.w, t1.w, c1.z",
"min t1.w, t1.w, c0.w",
"sub t1.w, c1.w, t1.w",
"mul t0.w, t0.w, t1.w",
"mov o0, t0",
];
diffuseProgram = context.createProgram();
opacityProgram = context.createProgram();
diffuseBlendProgram = context.createProgram();
opacityBlendProgram = context.createProgram();
var compiledVertexProgram:ByteArray = compileProgram(Context3DProgramType.VERTEX, vertexProgram);
diffuseProgram.upload(compiledVertexProgram, compileProgram(Context3DProgramType.FRAGMENT, fragmentDiffuseProgram));
opacityProgram.upload(compiledVertexProgram, compileProgram(Context3DProgramType.FRAGMENT, fragmentOpacityProgram));
diffuseBlendProgram.upload(compiledVertexProgram, compileProgram(Context3DProgramType.FRAGMENT, fragmentDiffuseBlendProgram));
opacityBlendProgram.upload(compiledVertexProgram, compileProgram(Context3DProgramType.FRAGMENT, fragmentOpacityBlendProgram));
}
private function compileProgram(mode:String, program:Array):ByteArray {
/*var string:String = "";
var length:int = program.length;
for (var i:int = 0; i < length; i++) {
var line:String = program[i];
string += line + ((i < length - 1) ? " \n" : "");
}*/
var proc:Procedure = new Procedure(program);
return proc.getByteCode(mode);
}
private function flush(camera:Camera3D):void {
if (fakeCounter == fake.length) fake[fakeCounter] = new Object3D();
var object:Object3D = fake[fakeCounter];
fakeCounter++;
object.localToCameraTransform.l = (za + zb)/2;
// Fill
drawUnit.object = object;
drawUnit.numTriangles = counter << 1;
if (blendDestination == Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA) {
drawUnit.program = (opacity != null) ? opacityProgram : diffuseProgram;
} else {
drawUnit.program = (opacity != null) ? opacityBlendProgram : diffuseBlendProgram;
}
// Set streams
drawUnit.setVertexBufferAt(0, vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
drawUnit.setVertexBufferAt(1, vertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_3);
// Set constants
drawUnit.setProjectionConstants(camera, 124);
drawUnit.setFragmentConstantsFromNumbers(0, ((fogColor >> 16) & 0xFF)/0xFF, ((fogColor >> 8) & 0xFF)/0xFF, (fogColor & 0xFF)/0xFF, fogMaxDensity);
drawUnit.setFragmentConstantsFromNumbers(1, fogNear, fogFar - fogNear, 0, 1);
// Set textures
drawUnit.setTextureAt(0, diffuse);
if (opacity != null) drawUnit.setTextureAt(1, opacity);
// Set blending
drawUnit.blendSource = blendSource;
drawUnit.blendDestination = blendDestination;
drawUnit.culling = Context3DTriangleFace.NONE;
// Send to render
camera.renderer.addDrawUnit(drawUnit, Renderer.TRANSPARENT_SORT);
}
private function drawParticleList(camera:Camera3D, list:Particle):void {
// Sorting
if (list.next != null) list = sortParticleList(list);
// Gather draws
var last:Particle;
for (var particle:Particle = list; particle != null; particle = particle.next) {
if (counter >= limit || particle.diffuse != diffuse || particle.opacity != opacity || particle.blendSource != blendSource || particle.blendDestination != blendDestination) {
if (drawUnit != null) flush(camera);
drawUnit = camera.renderer.createDrawUnit(null, null, indexBuffer, 0, 0);
diffuse = particle.diffuse;
opacity = particle.opacity;
blendSource = particle.blendSource;
blendDestination = particle.blendDestination;
counter = 0;
za = particle.z;
}
// Write constants
var offset:int = counter << 2;
drawUnit.setVertexConstantsFromNumbers(offset++, particle.originX, particle.originY, particle.width, particle.height);
drawUnit.setVertexConstantsFromNumbers(offset++, particle.x, particle.y, particle.z, particle.rotation);
drawUnit.setVertexConstantsFromNumbers(offset++, particle.uvScaleX, particle.uvScaleY, particle.uvOffsetX, particle.uvOffsetY);
drawUnit.setVertexConstantsFromNumbers(offset++, particle.red, particle.green, particle.blue, particle.alpha);
counter++;
zb = particle.z;
last = particle;
}
// Send to the collector
last.next = Particle.collector;
Particle.collector = list;
}
private function sortParticleList(list:Particle):Particle {
var left:Particle = list;
var right:Particle = list.next;
while (right != null && right.next != null) {
list = list.next;
right = right.next.next;
}
right = list.next;
list.next = null;
if (left.next != null) {
left = sortParticleList(left);
}
if (right.next != null) {
right = sortParticleList(right);
}
var flag:Boolean = left.z > right.z;
if (flag) {
list = left;
left = left.next;
} else {
list = right;
right = right.next;
}
var last:Particle = list;
while (true) {
if (left == null) {
last.next = right;
return list;
} else if (right == null) {
last.next = left;
return list;
}
if (flag) {
if (left.z > right.z) {
last = left;
left = left.next;
} else {
last.next = right;
last = right;
right = right.next;
flag = false;
}
} else {
if (right.z > left.z) {
last = right;
right = right.next;
} else {
last.next = left;
last = left;
left = left.next;
flag = true;
}
}
}
return null;
}
private function drawConflictEffects(camera:Camera3D, effectList:ParticleEffect):void {
var particleList:Particle;
for (var effect:ParticleEffect = effectList; effect != null; effect = next) {
var next:ParticleEffect = effect.next;
effect.next = null;
var last:Particle = effect.particleList;
while (last.next != null) last = last.next;
last.next = particleList;
particleList = effect.particleList;
effect.particleList = null;
if (camera.debug && effect.boundBox != null && (camera.checkInDebug(this) & Debug.BOUNDS)) Debug.drawBoundBox(camera, effect.aabb, localToCameraTransform, 0xFF0000);
}
drawParticleList(camera, particleList);
}
}
}

View File

@@ -0,0 +1,45 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.effects {
import alternativa.engine3d.resources.TextureResource;
/**
* @private
*/
public class TextureAtlas {
public var diffuse:TextureResource;
public var opacity:TextureResource;
public var columnsCount:int;
public var rowsCount:int;
public var rangeBegin:int;
public var rangeLength:int;
public var fps:int;
public var loop:Boolean;
public var originX:Number;
public var originY:Number;
public function TextureAtlas(diffuse:TextureResource, opacity:TextureResource = null, columnsCount:int = 1, rowsCount:int = 1, rangeBegin:int = 0, rangeLength:int = 1, fps:int = 30, loop:Boolean = false, originX:Number = 0.5, originY:Number = 0.5) {
this.diffuse = diffuse;
this.opacity = opacity;
this.columnsCount = columnsCount;
this.rowsCount = rowsCount;
this.rangeBegin = rangeBegin;
this.rangeLength = rangeLength;
this.fps = fps;
this.loop = loop;
this.originX = originX;
this.originY = originY;
}
}
}

View File

@@ -0,0 +1,64 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.lights {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Light3D;
import alternativa.engine3d.core.Object3D;
use namespace alternativa3d;
/**
* An ambient light source represents a fixed-intensity and fixed-color light source
* that affects all objects in the scene equally. Upon rendering, all objects in
* the scene are brightened with the specified intensity and color.
* This type of light source is mainly used to provide the scene with a basic view of the different objects in it.
*
* This description taken from http://en.wikipedia.org/wiki/Shading#Ambient_lighting
*/
public class AmbientLight extends Light3D {
/**
* Creates a AmbientLight object.
* @param color Light color.
*/
public function AmbientLight(color:uint) {
this.color = color;
}
/**
* Does not do anything.
*
*/
override public function calculateBoundBox():void {
}
/**
* @private
*/
override alternativa3d function calculateVisibility(camera:Camera3D):void {
camera.ambient[0] += ((color >> 16) & 0xFF)*intensity/255;
camera.ambient[1] += ((color >> 8) & 0xFF)*intensity/255;
camera.ambient[2] += (color & 0xFF)*intensity/255;
}
/**
* @inheritDoc
*/
override public function clone():Object3D {
var res:AmbientLight = new AmbientLight(color);
res.clonePropertiesFrom(this);
return res;
}
}
}

View File

@@ -0,0 +1,67 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.lights {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Light3D;
import alternativa.engine3d.core.Object3D;
use namespace alternativa3d;
/**
* A directional light source illuminates all objects equally from a given direction,
* like an area light of infinite size and infinite distance from the scene;
* there is shading, but cannot be any distance falloff.
*
* This description taken from http://en.wikipedia.org/wiki/Shading#Directional_lighting
*
* Lightning direction defines by z-axis of DirectionalLight.
* You can use lookAt() to make DirectionalLight point at given coordinates.
*/
public class DirectionalLight extends Light3D {
/**
* Creates a new instance.
* @param color Color of light source.
*/
public function DirectionalLight(color:uint) {
this.color = color;
}
/**
* Sets direction of DirectionalLight to given coordinates.
*/
public function lookAt(x:Number, y:Number, z:Number):void {
var dx:Number = x - this.x;
var dy:Number = y - this.y;
var dz:Number = z - this.z;
rotationX = Math.atan2(dz, Math.sqrt(dx*dx + dy*dy)) - Math.PI/2;
rotationY = 0;
rotationZ = -Math.atan2(dx, dy);
}
/**
* Does not do anything.
*/
override public function calculateBoundBox():void {
}
/**
* @inheritDoc
*/
override public function clone():Object3D {
var res:DirectionalLight = new DirectionalLight(color);
res.clonePropertiesFrom(this);
return res;
}
}
}

View File

@@ -0,0 +1,206 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.lights {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.BoundBox;
import alternativa.engine3d.core.Light3D;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Transform3D;
use namespace alternativa3d;
/**
* OmniLight is an attenuated light source placed at one point and spreads outward in all directions.
*
*/
public class OmniLight extends Light3D {
/**
* Distance from which falloff starts.
*/
public var attenuationBegin:Number;
/**
* Distance from at which falloff is complete.
*/
public var attenuationEnd:Number;
/**
* Creates a OmniLight object.
* @param color Light color.
* @param attenuationBegin Distance from which falloff starts.
* @param attenuationEnd Distance from at which falloff is complete.
*/
public function OmniLight(color:uint, attenuationBegin:Number, attenuationEnd:Number) {
this.color = color;
this.attenuationBegin = attenuationBegin;
this.attenuationEnd = attenuationEnd;
calculateBoundBox();
}
/**
* @private
*/
override alternativa3d function updateBoundBox(boundBox:BoundBox, transform:Transform3D = null):void {
if (transform != null) {
} else {
if (-attenuationEnd < boundBox.minX) boundBox.minX = -attenuationEnd;
if (attenuationEnd > boundBox.maxX) boundBox.maxX = attenuationEnd;
if (-attenuationEnd < boundBox.minY) boundBox.minY = -attenuationEnd;
if (attenuationEnd > boundBox.maxY) boundBox.maxY = attenuationEnd;
if (-attenuationEnd < boundBox.minZ) boundBox.minZ = -attenuationEnd;
if (attenuationEnd > boundBox.maxZ) boundBox.maxZ = attenuationEnd;
}
}
/**
* @private
*/
override alternativa3d function checkBound(targetObject:Object3D):Boolean {
var rScale:Number = Math.sqrt(lightToObjectTransform.a*lightToObjectTransform.a + lightToObjectTransform.e*lightToObjectTransform.e + lightToObjectTransform.i*lightToObjectTransform.i);
rScale += Math.sqrt(lightToObjectTransform.b*lightToObjectTransform.b + lightToObjectTransform.f*lightToObjectTransform.f + lightToObjectTransform.j*lightToObjectTransform.j);
rScale += Math.sqrt(lightToObjectTransform.c*lightToObjectTransform.c + lightToObjectTransform.g*lightToObjectTransform.g + lightToObjectTransform.k*lightToObjectTransform.k);
rScale /= 3;
rScale *= attenuationEnd;
rScale *= rScale;
var len:Number = 0;
var bb:BoundBox = targetObject.boundBox;
var minX:Number = bb.minX;
var minY:Number = bb.minY;
var minZ:Number = bb.minZ;
var maxX:Number = bb.maxX;
var px:Number = lightToObjectTransform.d;
var py:Number = lightToObjectTransform.h;
var pz:Number = lightToObjectTransform.l;
var maxY:Number = bb.maxY;
var maxZ:Number = bb.maxZ;
if (px < minX) {
if (py < minY) {
if (pz < minZ) {
len = (minX - px)*(minX - px) + (minY - py)*(minY - py) + (minZ - pz)*(minZ - pz);
return len < rScale;
} else if (pz < maxZ) {
len = (minX - px)*(minX - px) + (minY - py)*(minY - py);
return len < rScale;
} else if (pz > maxZ) {
len = (minX - px)*(minX - px) + (minY - py)*(minY - py) + (maxZ - pz)*(maxZ - pz);
return len < rScale;
}
} else if (py < maxY) {
if (pz < minZ) {
len = (minX - px)*(minX - px) + (minZ - pz)*(minZ - pz);
return len < rScale;
} else if (pz < maxZ) {
len = (minX - px)*(minX - px);
return len < rScale;
} else if (pz > maxZ) {
len = (minX - px)*(minX - px) + (maxZ - pz)*(maxZ - pz);
return len < rScale;
}
} else if (py > maxY) {
if (pz < minZ) {
len = (minX - px)*(minX - px) + (maxY - py)*(maxY - py) + (minZ - pz)*(minZ - pz);
return len < rScale;
} else if (pz < maxZ) {
len = (minX - px)*(minX - px) + (maxY - py)*(maxY - py);
return len < rScale;
} else if (pz > maxZ) {
len = (minX - px)*(minX - px) + (maxY - py)*(maxY - py) + (maxZ - pz)*(maxZ - pz);
return len < rScale;
}
}
} else if (px < maxX) {
if (py < minY) {
if (pz < minZ) {
len = (minY - py)*(minY - py) + (minZ - pz)*(minZ - pz);
return len < rScale;
} else if (pz < maxZ) {
len = (minY - py)*(minY - py);
return len < rScale;
} else if (pz > maxZ) {
len = (minY - py)*(minY - py) + (maxZ - pz)*(maxZ - pz);
return len < rScale;
}
} else if (py < maxY) {
if (pz < minZ) {
len = (minZ - pz)*(minZ - pz);
return len < rScale;
} else if (pz < maxZ) {
return true;
} else if (pz > maxZ) {
len = (maxZ - pz)*(maxZ - pz);
return len < rScale;
}
} else if (py > maxY) {
if (pz < minZ) {
len = (maxY - py)*(maxY - py) + (minZ - pz)*(minZ - pz);
return len < rScale;
} else if (pz < maxZ) {
len = (maxY - py)*(maxY - py);
return len < rScale;
} else if (pz > maxZ) {
len = (maxY - py)*(maxY - py) + (maxZ - pz)*(maxZ - pz);
return len < rScale;
}
}
} else if (px > maxX) {
if (py < minY) {
if (pz < minZ) {
len = (maxX - px)*(maxX - px) + (minY - py)*(minY - py) + (minZ - pz)*(minZ - pz);
return len < rScale;
} else if (pz < maxZ) {
len = (maxX - px)*(maxX - px) + (minY - py)*(minY - py);
return len < rScale;
} else if (pz > maxZ) {
len = (maxX - px)*(maxX - px) + (minY - py)*(minY - py) + (maxZ - pz)*(maxZ - pz);
return len < rScale;
}
} else if (py < maxY) {
if (pz < minZ) {
len = (maxX - px)*(maxX - px) + (minZ - pz)*(minZ - pz);
return len < rScale;
} else if (pz < maxZ) {
len = (maxX - px)*(maxX - px);
return len < rScale;
} else if (pz > maxZ) {
len = (maxX - px)*(maxX - px) + (maxZ - pz)*(maxZ - pz);
return len < rScale;
}
} else if (py > maxY) {
if (pz < minZ) {
len = (maxX - px)*(maxX - px) + (maxY - py)*(maxY - py) + (minZ - pz)*(minZ - pz);
return len < rScale;
} else if (pz < maxZ) {
len = (maxX - px)*(maxX - px) + (maxY - py)*(maxY - py);
return len < rScale;
} else if (pz > maxZ) {
len = (maxX - px)*(maxX - px) + (maxY - py)*(maxY - py) + (maxZ - pz)*(maxZ - pz);
return len < rScale;
}
}
}
return true;
}
/**
* @inheritDoc
*/
override public function clone():Object3D {
var res:OmniLight = new OmniLight(color, attenuationBegin, attenuationEnd);
res.clonePropertiesFrom(this);
return res;
}
}
}

View File

@@ -0,0 +1,211 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.lights {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.BoundBox;
import alternativa.engine3d.core.Light3D;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Transform3D;
use namespace alternativa3d;
/**
* OmniLight is an attenuated light source placed at one point and spreads outward in a coned direction.
*
* Lightning direction defines by z-axis of OmniLight.
* You can use lookAt() to make DirectionalLight point at given coordinates.
*/
public class SpotLight extends Light3D {
/**
* Distance from which falloff starts.
*/
public var attenuationBegin:Number;
/**
* Distance from at which falloff is complete.
*/
public var attenuationEnd:Number;
/**
* Adjusts the angle of a light's cone.
*/
public var hotspot:Number;
/**
* Adjusts the angle of a light's falloff. For photometric lights, the Field angle is comparable
* to the Falloff angle. It is the angle at which the light's intensity has fallen to zero.
*/
public var falloff:Number;
/**
* Creates a new SpotLight instance.
* @param color Light color.
* @param attenuationBegin Distance from which falloff starts.
* @param attenuationEnd Distance from at which falloff is complete.
* @param hotspot Adjusts the angle of a light's cone. The Hotspot value is measured in radians.
* @param falloff Adjusts the angle of a light's falloff. The Falloff value is measured in radians.
*/
public function SpotLight(color:uint, attenuationBegin:Number, attenuationEnd:Number, hotspot:Number, falloff:Number) {
this.color = color;
this.attenuationBegin = attenuationBegin;
this.attenuationEnd = attenuationEnd;
this.hotspot = hotspot;
this.falloff = falloff;
calculateBoundBox();
}
/**
* @private
*/
override alternativa3d function updateBoundBox(boundBox:BoundBox, transform:Transform3D = null):void {
var r:Number = (falloff < Math.PI) ? Math.sin(falloff*0.5)*attenuationEnd : attenuationEnd;
var bottom:Number = (falloff < Math.PI) ? 0 : Math.cos(falloff*0.5)*attenuationEnd;
boundBox.minX = -r;
boundBox.minY = -r;
boundBox.minZ = bottom;
boundBox.maxX = r;
boundBox.maxY = r;
boundBox.maxZ = attenuationEnd;
}
/**
* Set direction of the light direction to the given coordinates..
*/
public function lookAt(x:Number, y:Number, z:Number):void {
var dx:Number = x - this.x;
var dy:Number = y - this.y;
var dz:Number = z - this.z;
rotationX = Math.atan2(dz, Math.sqrt(dx*dx + dy*dy)) - Math.PI/2;
rotationY = 0;
rotationZ = -Math.atan2(dx, dy);
}
/**
* @private
*/
override alternativa3d function checkBound(targetObject:Object3D):Boolean {
var minX:Number = boundBox.minX;
var minY:Number = boundBox.minY;
var minZ:Number = boundBox.minZ;
var maxX:Number = boundBox.maxX;
var maxY:Number = boundBox.maxY;
var maxZ:Number = boundBox.maxZ;
var sum:Number;
var pro:Number;
// Half sizes of the source's boundbox
var w:Number = (maxX - minX)*0.5;
var l:Number = (maxY - minY)*0.5;
var h:Number = (maxZ - minZ)*0.5;
// Half-vectors of the source's boundbox
var ax:Number = lightToObjectTransform.a*w;
var ay:Number = lightToObjectTransform.e*w;
var az:Number = lightToObjectTransform.i*w;
var bx:Number = lightToObjectTransform.b*l;
var by:Number = lightToObjectTransform.f*l;
var bz:Number = lightToObjectTransform.j*l;
var cx:Number = lightToObjectTransform.c*h;
var cy:Number = lightToObjectTransform.g*h;
var cz:Number = lightToObjectTransform.k*h;
// Half sizes of the boundboxes
var objectBB:BoundBox = targetObject.boundBox;
var hw:Number = (objectBB.maxX - objectBB.minX)*0.5;
var hl:Number = (objectBB.maxY - objectBB.minY)*0.5;
var hh:Number = (objectBB.maxZ - objectBB.minZ)*0.5;
// Vector between centers of the bounboxes
var dx:Number = lightToObjectTransform.a*(minX + w) + lightToObjectTransform.b*(minY + l) + lightToObjectTransform.c*(minZ + h) + lightToObjectTransform.d - objectBB.minX - hw;
var dy:Number = lightToObjectTransform.e*(minX + w) + lightToObjectTransform.f*(minY + l) + lightToObjectTransform.g*(minZ + h) + lightToObjectTransform.h - objectBB.minY - hl;
var dz:Number = lightToObjectTransform.i*(minX + w) + lightToObjectTransform.j*(minY + l) + lightToObjectTransform.k*(minZ + h) + lightToObjectTransform.l - objectBB.minZ - hh;
// X of the object
sum = 0;
if (ax >= 0) sum += ax; else sum -= ax;
if (bx >= 0) sum += bx; else sum -= bx;
if (cx >= 0) sum += cx; else sum -= cx;
sum += hw;
if (dx >= 0) sum -= dx;
sum += dx;
if (sum <= 0) return false;
// Y of the object
sum = 0;
if (ay >= 0) sum += ay; else sum -= ay;
if (by >= 0) sum += by; else sum -= by;
if (cy >= 0) sum += cy; else sum -= cy;
sum += hl;
if (dy >= 0) sum -= dy; else sum += dy;
if (sum <= 0) return false;
// Z of the object
sum = 0;
if (az >= 0) sum += az; else sum -= az;
if (bz >= 0) sum += bz; else sum -= bz;
if (cz >= 0) sum += cz; else sum -= cz;
sum += hl;
if (dz >= 0) sum -= dz; else sum += dz;
if (sum <= 0) return false;
// X of the source
sum = 0;
pro = lightToObjectTransform.a*ax + lightToObjectTransform.e*ay + lightToObjectTransform.i*az;
if (pro >= 0) sum += pro; else sum -= pro;
pro = lightToObjectTransform.a*bx + lightToObjectTransform.e*by + lightToObjectTransform.i*bz;
if (pro >= 0) sum += pro; else sum -= pro;
pro = lightToObjectTransform.a*cx + lightToObjectTransform.e*cy + lightToObjectTransform.i*cz;
if (pro >= 0) sum += pro; else sum -= pro;
if (lightToObjectTransform.a >= 0) sum += lightToObjectTransform.a*hw; else sum -= lightToObjectTransform.a*hw;
if (lightToObjectTransform.e >= 0) sum += lightToObjectTransform.e*hl; else sum -= lightToObjectTransform.e*hl;
if (lightToObjectTransform.i >= 0) sum += lightToObjectTransform.i*hh; else sum -= lightToObjectTransform.i*hh;
pro = lightToObjectTransform.a*dx + lightToObjectTransform.e*dy + lightToObjectTransform.i*dz;
if (pro >= 0) sum -= pro; else sum += pro;
if (sum <= 0) return false;
// Y of the source
sum = 0;
pro = lightToObjectTransform.b*ax + lightToObjectTransform.f*ay + lightToObjectTransform.j*az;
if (pro >= 0) sum += pro; else sum -= pro;
pro = lightToObjectTransform.b*bx + lightToObjectTransform.f*by + lightToObjectTransform.j*bz;
if (pro >= 0) sum += pro; else sum -= pro;
pro = lightToObjectTransform.b*cx + lightToObjectTransform.f*cy + lightToObjectTransform.j*cz;
if (pro >= 0) sum += pro; else sum -= pro;
if (lightToObjectTransform.b >= 0) sum += lightToObjectTransform.b*hw; else sum -= lightToObjectTransform.b*hw;
if (lightToObjectTransform.f >= 0) sum += lightToObjectTransform.f*hl; else sum -= lightToObjectTransform.f*hl;
if (lightToObjectTransform.j >= 0) sum += lightToObjectTransform.j*hh; else sum -= lightToObjectTransform.j*hh;
pro = lightToObjectTransform.b*dx + lightToObjectTransform.f*dy + lightToObjectTransform.j*dz;
if (pro >= 0) sum -= pro;
sum += pro;
if (sum <= 0) return false;
// Z of the source
sum = 0;
pro = lightToObjectTransform.c*ax + lightToObjectTransform.g*ay + lightToObjectTransform.k*az;
if (pro >= 0) sum += pro; else sum -= pro;
pro = lightToObjectTransform.c*bx + lightToObjectTransform.g*by + lightToObjectTransform.k*bz;
if (pro >= 0) sum += pro; else sum -= pro;
pro = lightToObjectTransform.c*cx + lightToObjectTransform.g*cy + lightToObjectTransform.k*cz;
if (pro >= 0) sum += pro; else sum -= pro;
if (lightToObjectTransform.c >= 0) sum += lightToObjectTransform.c*hw; else sum -= lightToObjectTransform.c*hw;
if (lightToObjectTransform.g >= 0) sum += lightToObjectTransform.g*hl; else sum -= lightToObjectTransform.g*hl;
if (lightToObjectTransform.k >= 0) sum += lightToObjectTransform.k*hh; else sum -= lightToObjectTransform.k*hh;
pro = lightToObjectTransform.c*dx + lightToObjectTransform.g*dy + lightToObjectTransform.k*dz;
if (pro >= 0) sum -= pro; else sum += pro;
if (sum <= 0) return false;
// TODO: checking on random axises
return true;
}
/**
* @inheritDoc
*/
override public function clone():Object3D {
var res:SpotLight = new SpotLight(color, attenuationBegin, attenuationEnd, hotspot, falloff);
res.clonePropertiesFrom(this);
return res;
}
}
}

View File

@@ -0,0 +1,633 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.animation.AnimationClip;
import alternativa.engine3d.animation.keys.TransformKey;
import alternativa.engine3d.animation.keys.TransformTrack;
import alternativa.engine3d.core.BoundBox;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Transform3D;
import alternativa.engine3d.core.VertexAttributes;
import alternativa.engine3d.core.VertexStream;
import alternativa.engine3d.lights.AmbientLight;
import alternativa.engine3d.lights.DirectionalLight;
import alternativa.engine3d.lights.OmniLight;
import alternativa.engine3d.lights.SpotLight;
import alternativa.engine3d.materials.LightMapMaterial;
import alternativa.engine3d.materials.Material;
import alternativa.engine3d.materials.StandardMaterial;
import alternativa.engine3d.materials.TextureMaterial;
import alternativa.engine3d.objects.Joint;
import alternativa.engine3d.objects.Mesh;
import alternativa.engine3d.objects.Skin;
import alternativa.engine3d.objects.Surface;
import alternativa.engine3d.resources.ExternalTextureResource;
import alternativa.engine3d.resources.Geometry;
import alternativa.engine3d.resources.TextureResource;
import alternativa.osgi.OSGi;
import alternativa.osgi.service.clientlog.IClientLog;
import alternativa.protocol.CompressionType;
import alternativa.protocol.ICodec;
import alternativa.protocol.IProtocol;
import alternativa.protocol.OptionalMap;
import alternativa.protocol.ProtocolBuffer;
import alternativa.protocol.impl.PacketHelper;
import alternativa.protocol.impl.Protocol;
import alternativa.protocol.info.TypeCodecInfo;
import alternativa.protocol.osgi.ProtocolActivator;
import alternativa.types.Long;
import commons.A3DMatrix;
import flash.geom.Matrix3D;
import flash.utils.ByteArray;
import flash.utils.Dictionary;
import flash.utils.Endian;
import platform.client.formats.a3d.osgi.Activator;
import platform.clients.fp10.libraries.alternativaprotocol.Activator;
import versions.version2.a3d.A3D2;
import versions.version2.a3d.animation.A3D2AnimationClip;
import versions.version2.a3d.animation.A3D2Keyframe;
import versions.version2.a3d.animation.A3D2Track;
import versions.version2.a3d.geometry.A3D2IndexBuffer;
import versions.version2.a3d.geometry.A3D2VertexAttributes;
import versions.version2.a3d.geometry.A3D2VertexBuffer;
import versions.version2.a3d.materials.A3D2Image;
import versions.version2.a3d.materials.A3D2Map;
import versions.version2.a3d.materials.A3D2Material;
import versions.version2.a3d.objects.A3D2AmbientLight;
import versions.version2.a3d.objects.A3D2Box;
import versions.version2.a3d.objects.A3D2DirectionalLight;
import versions.version2.a3d.objects.A3D2Joint;
import versions.version2.a3d.objects.A3D2JointBindTransform;
import versions.version2.a3d.objects.A3D2Mesh;
import versions.version2.a3d.objects.A3D2Object;
import versions.version2.a3d.objects.A3D2OmniLight;
import versions.version2.a3d.objects.A3D2Skin;
import versions.version2.a3d.objects.A3D2SpotLight;
import versions.version2.a3d.objects.A3D2Surface;
import versions.version2.a3d.objects.A3D2Transform;
use namespace alternativa3d;
/**
* An object which allows to convert hierarchy of three-dimensional objects to binary A3D format.
*/
public class ExporterA3D {
private var wasInit:Boolean = false;
private var protocol:Protocol;
private var parents:Dictionary;
private var geometries:Dictionary;
private var images:Dictionary;
private var indexBufferID:int;
private var materialID:int;
private var mapID:int;
private var imageID:int;
private var animationID:int;
private var trackID:int;
private var vertexBufferID:int;
private var boxID:int;
private var tracksMap:Dictionary;
private var materialsMap:Dictionary;
private var mapsMap:Dictionary;
alternativa3d var idGenerator:IIDGenerator = new IncrementalIDGenerator();
/**
* Creates an instance of ExporterA3D.
*/
public function ExporterA3D() {
init();
}
private function init():void {
if (wasInit) return;
if (OSGi.getInstance() != null) {
protocol = Protocol(OSGi.getInstance().getService(IProtocol));
return;
}
OSGi.clientLog = new DummyClientLog();
var osgi:OSGi = new OSGi();
osgi.registerService(IClientLog, new DummyClientLog());
new ProtocolActivator().start(osgi);
new platform.client.formats.a3d.osgi.Activator().start(osgi);
new platform.clients.fp10.libraries.alternativaprotocol.Activator().start(osgi);
protocol = Protocol(osgi.getService(IProtocol));
wasInit = true;
}
/**
* Exports a scene to A3D format.
* @param root Root object of scene.
* @return Data in A3D format.
*/
public function export(root:Object3D = null, animations:Vector.<AnimationClip> = null):ByteArray {
boxID = 0;
indexBufferID = 0;
vertexBufferID = 0;
materialID = 0;
mapID = 0;
imageID = 0;
animationID = 0;
materialsMap = new Dictionary();
mapsMap = new Dictionary();
geometries = new Dictionary();
tracksMap = new Dictionary();
images = new Dictionary();
parents = new Dictionary();
var a3D:A3D2 = new A3D2(
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null
);
if (root != null) {
exportHierarchy(root, a3D);
}
if (animations != null) {
for each (var animation:AnimationClip in animations) {
exportAnimation(animation, a3D);
}
}
var data:ByteArray = new ByteArray();
var result:ByteArray = new ByteArray();
var codec:ICodec = protocol.getCodec(new TypeCodecInfo(A3D2, false));
var protocolBuffer:ProtocolBuffer = new ProtocolBuffer(data, data, new OptionalMap());
data.writeShort(2);
data.writeShort(0);
codec.encode(protocolBuffer, a3D);
data.position = 0;
PacketHelper.wrapPacket(result, protocolBuffer, CompressionType.DEFLATE);
return result;
}
private function exportAnimation(source:AnimationClip, dest:A3D2):void {
var anim:A3D2AnimationClip = new A3D2AnimationClip(animationID, source.loop, source.name, null, exportTracks(source, dest));
if (dest.animationClips == null) dest.animationClips = new Vector.<A3D2AnimationClip>();
dest.animationClips[animationID] = anim; animationID++;
}
private function exportTracks(source:AnimationClip, dest:A3D2):Vector.<int> {
var id:int;
var result:Vector.<int> = new Vector.<int>();
for (var i:int = 0; i < source.numTracks; i++) {
var t:TransformTrack = source.getTrackAt(i) as TransformTrack;
if (t != null && tracksMap[t] == null) {
id = trackID++;
var exportTrack:A3D2Track = new A3D2Track(id, exportKeyframes(t), t.object);
tracksMap[t] = id;
if (dest.animationTracks == null) dest.animationTracks = new Vector.<A3D2Track>();
dest.animationTracks[id] = exportTrack;
} else {
id = tracksMap[t];
}
result.push(id);
}
return result;
}
private function exportKeyframes(source:TransformTrack):Vector.<A3D2Keyframe> {
var result:Vector.<A3D2Keyframe> = new Vector.<A3D2Keyframe>();
for (var key:TransformKey = TransformKey(source.keyFramesList); key.next != null; key = key.next) {
var exportKey:A3D2Keyframe = new A3D2Keyframe(key._time, exportTransformFromKeyframe(key));
result.push(exportKey);
}
return result;
}
private function exportTransformFromKeyframe(key:TransformKey):A3D2Transform {
var m:Matrix3D = Matrix3D(key.value);
var vec:Vector.<Number> = m.rawData;
var exportTransform:A3D2Transform = new A3D2Transform(
new A3DMatrix(
vec[0], vec[4], vec[8], vec[12],
vec[1], vec[5], vec[9], vec[13],
vec[2], vec[6], vec[10], vec[14]
));
return exportTransform;
}
private function exportHierarchy(source:Object3D, dest:A3D2):void {
var id:Long = idGenerator.getID(source);
if (source.transformChanged) {
source.composeTransforms();
}
if (source is SpotLight) {
exportSpotLight(id, source as SpotLight, dest);
} else if (source is OmniLight) {
exportOmniLight(id, source as OmniLight, dest);
} else if (source is DirectionalLight) {
exportDirLight(id, source as DirectionalLight, dest);
} else if (source is AmbientLight) {
exportAmbientLight(id, source as AmbientLight, dest);
} else if (source is Skin) {
exportSkin(id, source as Skin, dest);
} else if (source is Mesh) {
exportMesh(id, source as Mesh, dest);
} else if (source is Joint) {
exportJoint(id, source as Joint, dest);
} else if (source is Object3D) {
exportObject3D(id, source, dest);
} else {
trace("Unsupported object type", source);
}
parents[source] = id;
for (var child:Object3D = source.childrenList; child != null; child = child.next) {
exportHierarchy(child, dest);
}
}
private function exportJoint(id:Long, source:Joint, dest:A3D2):void {
var a3DObject:A3D2Joint = new A3D2Joint(
exportBoundBox(source.boundBox, dest),
id,
source.name,
parents[source.parent is Skin ? source.parent.parent : source.parent],
exportTransform(source.transform),
source.visible
);
if (dest.joints == null) dest.joints = new Vector.<A3D2Joint>();
dest.joints.push(a3DObject);
}
private function exportObject3D(id:Long, source:Object3D, dest:A3D2):void {
var a3DObject:A3D2Object = new A3D2Object(
exportBoundBox(source.boundBox, dest),
id,
source.name,
parents[source.parent],
exportTransform(source.transform),
source.visible
);
if (dest.objects == null) dest.objects = new Vector.<A3D2Object>();
dest.objects.push(a3DObject);
}
private function exportSpotLight(id:Long, source:SpotLight, dest:A3D2):void {
var a3DObject:A3D2SpotLight = new A3D2SpotLight(
source.attenuationBegin,
source.attenuationEnd,
exportBoundBox(source.boundBox, dest),
source.color,
source.falloff,
source.hotspot,
id,
source.intensity,
source.name,
parents[source.parent],
exportTransform(source.transform),
source.visible
);
if (dest.spotLights == null) dest.spotLights = new Vector.<A3D2SpotLight>();
dest.spotLights.push(a3DObject);
}
private function exportOmniLight(id:Long, source:OmniLight, dest:A3D2):void {
var a3DObject:A3D2OmniLight = new A3D2OmniLight(
source.attenuationBegin,
source.attenuationEnd,
exportBoundBox(source.boundBox, dest),
source.color,
id,
source.intensity,
source.name,
parents[source.parent],
exportTransform(source.transform),
source.visible
);
if (dest.omniLights == null) dest.omniLights = new Vector.<A3D2OmniLight>();
dest.omniLights.push(a3DObject);
}
private function exportDirLight(id:Long, source:DirectionalLight, dest:A3D2):void {
var a3DObject:A3D2DirectionalLight = new A3D2DirectionalLight(
exportBoundBox(source.boundBox, dest),
source.color,
id,
source.intensity,
source.name,
parents[source.parent],
exportTransform(source.transform),
source.visible
);
if (dest.directionalLights == null) dest.directionalLights = new Vector.<A3D2DirectionalLight>();
dest.directionalLights.push(a3DObject);
}
private function exportAmbientLight(id:Long, source:AmbientLight, dest:A3D2):void {
var a3DObject:A3D2AmbientLight = new A3D2AmbientLight(
exportBoundBox(source.boundBox, dest),
source.color,
id,
source.intensity,
source.name,
parents[source.parent],
exportTransform(source.transform),
source.visible
);
if (dest.ambientLights == null) dest.ambientLights = new Vector.<A3D2AmbientLight>();
dest.ambientLights.push(a3DObject);
}
private function exportMesh(id:Long, source:Mesh, dest:A3D2):void {
var geometryData:GeometryData = exportGeometry(source.geometry, dest);
var a3DMesh:A3D2Mesh = new A3D2Mesh(
exportBoundBox(source.boundBox, dest),
id,
geometryData.indexBufferID,
source.name,
parents[source.parent],
exportSurfaces(source._surfaces, dest),
exportTransform(source.transform),
geometryData.vertexBufferIDs,
source.visible
);
if (dest.meshes == null) dest.meshes = new Vector.<A3D2Mesh>();
dest.meshes.push(a3DMesh);
}
private function exportSkin(id:Long, source:Skin, dest:A3D2):A3D2Skin {
var geometryData:GeometryData = exportGeometry(source.geometry, dest);
var a3DSkin:A3D2Skin = new A3D2Skin(
exportBoundBox(source.boundBox, dest),
id,
geometryData.indexBufferID,
exportJointsBindTransforms(source._renderedJoints),
exportJointsListFromSurfacesJoints(source.surfaceJoints),
source.name,
exportNumJoitns(source.surfaceJoints),
null,
exportSurfaces(source._surfaces, dest),
exportTransform(source.transform),
geometryData.vertexBufferIDs,
source.visible
);
if (dest.skins == null) dest.skins = new Vector.<A3D2Skin>();
dest.skins.push(a3DSkin);
return a3DSkin;
}
private function exportNumJoitns(surfaceJoints:Vector.<Vector.<Joint>>):Vector.<uint> {
var result:Vector.<uint> = new Vector.<uint>();
for (var i:int = 0; i < surfaceJoints.length; i++) {
result.push(surfaceJoints[i].length);
}
return result;
}
private function exportJointsBindTransforms(joints:Vector.<Joint>):Vector.<A3D2JointBindTransform> {
var result:Vector.<A3D2JointBindTransform> = new Vector.<A3D2JointBindTransform>();
for each (var joint:Joint in joints) {
result.push(new A3D2JointBindTransform(exportTransform(joint.bindPoseTransform), idGenerator.getID(joint)));
}
return result;
}
private function exportJointsListFromSurfacesJoints(surfaceJoints:Vector.<Vector.<Joint>>):Vector.<Long> {
var result:Vector.<Long> = new Vector.<Long>();
for (var i:int = 0; i < surfaceJoints.length; i++) {
var joints:Vector.<Joint> = surfaceJoints[i];
for each (var joint:Joint in joints) {
result.push(idGenerator.getID(joint));
}
}
return result;
}
private function exportSurfaces(surfaces:Vector.<Surface>, dest:A3D2):Vector.<A3D2Surface> {
var result:Vector.<A3D2Surface> = new Vector.<A3D2Surface>();
for (var i:int = 0, count:int = surfaces.length; i < count; i++) {
var surface:Surface = surfaces[i];
var resSurface:A3D2Surface = new A3D2Surface(surface.indexBegin, exportMaterial(surface.material, dest), surface.numTriangles);
result[i] = resSurface;
}
return result;
}
private function exportMaterial(source:Material, dest:A3D2):int {
if (source == null) return -1;
var result:A3D2Material = materialsMap[source];
if (result != null) return result.id;
if (source is ParserMaterial) {
var parserMaterial:ParserMaterial = source as ParserMaterial;
result = new A3D2Material(
exportMap(parserMaterial.textures["diffuse"], 0, dest),
exportMap(parserMaterial.textures["glossiness"], 0, dest),
materialID,
exportMap(parserMaterial.textures["emission"], 0, dest),
exportMap(parserMaterial.textures["bump"], 0, dest),
exportMap(parserMaterial.textures["transparent"], 0, dest),
-1,
exportMap(parserMaterial.textures["specular"], 0, dest)
);
} else if (source is LightMapMaterial) {
var lightMapMaterial:LightMapMaterial = source as LightMapMaterial;
result = new A3D2Material(
exportMap(lightMapMaterial.diffuseMap, 0, dest),
-1,
materialID,
exportMap(lightMapMaterial.lightMap, lightMapMaterial.lightMapChannel, dest),
-1,
exportMap(lightMapMaterial.opacityMap, 0, dest),
-1,
-1);
} else if (source is StandardMaterial) {
var standardMaterial:StandardMaterial = source as StandardMaterial;
result = new A3D2Material(
exportMap(standardMaterial.diffuseMap, 0, dest),
exportMap(standardMaterial.glossinessMap, 0, dest), materialID,
-1,
exportMap(standardMaterial.normalMap, 0, dest),
exportMap(standardMaterial.opacityMap, 0, dest),
-1,
exportMap(standardMaterial.specularMap, 0, dest));
} else if (source is TextureMaterial) {
var textureMaterial:TextureMaterial = source as TextureMaterial;
result = new A3D2Material(exportMap(textureMaterial.diffuseMap, 0, dest), -1, materialID, -1, -1, exportMap(textureMaterial.opacityMap, 0, dest), -1, -1);
}
materialsMap[source] = result;
if (dest.materials == null) dest.materials = new Vector.<A3D2Material>();
dest.materials[materialID] = result;
return materialID++;
}
private function exportMap(source:TextureResource, channel:int, dest:A3D2):int {
if (source == null) return -1;
var result:A3D2Map = mapsMap[source];
if (result != null) return result.id;
if (source is ExternalTextureResource) {
var resource:ExternalTextureResource = source as ExternalTextureResource;
result = new A3D2Map(channel, mapID, exportImage(resource, dest));
if (dest.maps == null) dest.maps = new Vector.<A3D2Map>();
dest.maps[mapID] = result;
mapsMap[source] = result;
return mapID++;
}
return -1;
}
private function exportImage(source:ExternalTextureResource, dest:A3D2):int {
var image:Object = images[source];
if (image != null) return int(image);
var result:A3D2Image = new A3D2Image(imageID, source.url);
if (dest.images == null) dest.images = new Vector.<A3D2Image>();
dest.images[imageID] = result;
return imageID++;
}
private function exportGeometry(geometry:Geometry, dest:A3D2):GeometryData {
var result:GeometryData = geometries[geometry];
if (result != null) return result;
result = new GeometryData();
result.vertexBufferIDs = new Vector.<int>();
var indicesData:ByteArray = new ByteArray();
indicesData.endian = Endian.LITTLE_ENDIAN;
var indices:Vector.<uint> = geometry.indices;
for (var i:int = 0, count:int = indices.length; i < count; i++) {
indicesData.writeShort(indices[i]);
}
var indexBuffer:A3D2IndexBuffer = new A3D2IndexBuffer(indicesData, indexBufferID, indices.length);
result.indexBufferID = indexBufferID;
if (dest.indexBuffers == null) dest.indexBuffers = new Vector.<A3D2IndexBuffer>();
dest.indexBuffers[indexBufferID] = indexBuffer;
indexBufferID++;
for (i = 0,count = geometry._vertexStreams.length; i < count; i++) {
var stream:VertexStream = geometry._vertexStreams[i];
var buffer:A3D2VertexBuffer = new A3D2VertexBuffer(exportAttributes(stream.attributes), stream.data, vertexBufferID, geometry.numVertices);
if (dest.vertexBuffers == null) dest.vertexBuffers = new Vector.<A3D2VertexBuffer>();
dest.vertexBuffers[vertexBufferID] = buffer;
result.vertexBufferIDs[i] = vertexBufferID++;
}
return result;
}
private function exportAttributes(attributes:Array):Vector.<A3D2VertexAttributes> {
var prev:int = -1;
var result:Vector.<A3D2VertexAttributes> = new Vector.<A3D2VertexAttributes>();
for each (var attr:int in attributes) {
if (attr == prev) continue;
switch (attr) {
case VertexAttributes.POSITION:
result.push(A3D2VertexAttributes.POSITION);
break;
case VertexAttributes.NORMAL:
result.push(A3D2VertexAttributes.NORMAL);
break;
case VertexAttributes.TANGENT4:
result.push(A3D2VertexAttributes.TANGENT4);
break;
default:
if ((attr >= VertexAttributes.JOINTS[0]) && (attr <= VertexAttributes.JOINTS[3])) {
result.push(A3D2VertexAttributes.JOINT);
} else if ((attr >= VertexAttributes.TEXCOORDS[0]) && (attr <= VertexAttributes.TEXCOORDS[7])) {
result.push(A3D2VertexAttributes.TEXCOORD);
}
break;
}
prev = attr;
}
return result;
}
private function exportTransform(source:Transform3D):A3D2Transform {
return new A3D2Transform(new A3DMatrix(
source.a, source.b, source.c, source.d,
source.e, source.f, source.g, source.h,
source.i, source.j, source.k, source.l
));
}
private function exportBoundBox(boundBox:BoundBox, dest:A3D2):int {
if (boundBox == null) return -1;
if (dest.boxes == null) dest.boxes = new Vector.<A3D2Box>();
dest.boxes[boxID] = new A3D2Box(Vector.<Number>([boundBox.minX, boundBox.minY, boundBox.minZ, boundBox.maxX, boundBox.maxY, boundBox.maxZ]), boxID);
return boxID++;
}
}
}
import alternativa.osgi.service.clientlog.IClientLog;
import alternativa.osgi.service.clientlog.IClientLogChannelListener;
class GeometryData {
public var indexBufferID:int;
public var vertexBufferIDs:Vector.<int>;
public function GeometryData(indexBufferID:int = -1, vertexBufferIDs:Vector.<int> = null) {
this.indexBufferID = indexBufferID;
this.vertexBufferIDs = vertexBufferIDs;
}
}
class DummyClientLog implements IClientLog {
public function logError(channelName:String, text:String, ... vars):void {
}
public function log(channelName:String, text:String, ... rest):void {
}
public function getChannelStrings(channelName:String):Vector.<String> {
return null;
}
public function addLogListener(listener:IClientLogChannelListener):void {
}
public function removeLogListener(listener:IClientLogChannelListener):void {
}
public function addLogChannelListener(channelName:String, listener:IClientLogChannelListener):void {
}
public function removeLogChannelListener(channelName:String, listener:IClientLogChannelListener):void {
}
public function getChannelNames():Vector.<String> {
return null;
}
}

View File

@@ -0,0 +1,23 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders {
import alternativa.engine3d.core.Object3D;
import alternativa.types.Long;
/**
* @private
*/
public interface IIDGenerator {
function getID(object:Object3D):Long;
}
}

View File

@@ -0,0 +1,38 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders {
import alternativa.engine3d.core.Object3D;
import alternativa.types.Long;
import flash.utils.Dictionary;
/**
* @private
*/
public class IncrementalIDGenerator implements IIDGenerator {
private var lastID:uint = 0;
private var objects:Dictionary;
public function IncrementalIDGenerator() {
objects = new Dictionary(true);
}
public function getID(object:Object3D):Long {
var result:Long = objects[object];
if (result == null) {
result = objects[object] = Long.fromInt(lastID); lastID++;
}
return result;
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,188 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders {
import alternativa.engine3d.alternativa3d;
import alternativa.osgi.OSGi;
import alternativa.osgi.service.clientlog.IClientLog;
import alternativa.protocol.ICodec;
import alternativa.protocol.IProtocol;
import alternativa.protocol.OptionalMap;
import alternativa.protocol.ProtocolBuffer;
import alternativa.protocol.impl.OptionalMapCodecHelper;
import alternativa.protocol.impl.PacketHelper;
import alternativa.protocol.impl.Protocol;
import alternativa.protocol.info.TypeCodecInfo;
import alternativa.protocol.osgi.ProtocolActivator;
import flash.utils.ByteArray;
import platform.client.formats.a3d.osgi.Activator;
import platform.clients.fp10.libraries.alternativaprotocol.Activator;
import versions.version1.a3d.A3D;
import versions.version2.a3d.A3D2;
import versions.version2.a3d.A3D2Extra1;
import versions.version2.a3d.A3D2Extra2;
use namespace alternativa3d;
/**
* A parser for loading models of A3D binary format.
* A3D format reference you can find <a href="http://alternativaplatform.com/public/A3DFormat_en.pdf">here</a>.
*/
public class ParserA3D extends Parser {
// static public const logChannel:String = "ParserLog";
private var protocol:Protocol;
private var wasInit:Boolean = false;
/**
* Creates a new instance of ParserA3D.
*
*/
public function ParserA3D() {
init();
}
/**
* Parses model of a3d format, that is passed as byteArray to <code>input</code> parameter, then fills the arrays <code>objects</code> and <code>hierarchy</code> by the instances of three-dimensional objects.
* @param input <code>ByteArray</code> consists of A3D data.
*/
public function parse(input:ByteArray):void {
try {
input.position = 0;
var version:int = input.readByte();
if (version == 0) {
// For the 1st version of format
parseVersion1(input);
} else {
// For the 2nd version of format and above, the first byte contains length of file and flag bits.
// Bit of packing. It always equal to 1, because version 2 and above is always packed.
parseVersionOver1(input);
}
} catch (e:Error) {
e.message = "Parsing failed: " + e.message;
throw e;
}
}
private function init():void {
if (wasInit) return;
var osgi:OSGi;
if (OSGi.getInstance() == null) {
osgi = new OSGi();
OSGi.clientLog = new DummyClientLog();
osgi.registerService(IClientLog, new DummyClientLog());
new ProtocolActivator().start(osgi);
new platform.clients.fp10.libraries.alternativaprotocol.Activator().start(osgi);
} else {
osgi = OSGi.getInstance();
}
new platform.client.formats.a3d.osgi.Activator().start(osgi);
protocol = Protocol(osgi.getService(IProtocol));
wasInit = true;
}
private function parseVersion1(input:ByteArray):void {
input.position = 4;
var nullMap:OptionalMap = OptionalMapCodecHelper.decodeNullMap(input);
nullMap.setReadPosition(0);
var data:ByteArray = new ByteArray();
data.writeBytes(input, input.position);
data.position = 0;
var buffer:ProtocolBuffer = new ProtocolBuffer(data, data, nullMap);
var codec:ICodec = protocol.getCodec(new TypeCodecInfo(A3D, false));
var _a3d:A3D = A3D(codec.decode(buffer));
complete(_a3d);
}
private function parseVersionOver1(input:ByteArray):void {
input.position = 0;
var data:ByteArray = new ByteArray();
var buffer:ProtocolBuffer = new ProtocolBuffer(data, data, new OptionalMap());
PacketHelper.unwrapPacket(input, buffer);
input.position = 0;
var versionMajor:int = buffer.reader.readUnsignedShort();
var versionMinor:int = buffer.reader.readUnsignedShort();
switch (versionMajor) {
case 2:
if (versionMinor >= 6) {
compressedBuffers = true;
}
var parts:Vector.<Object> = new Vector.<Object>();
parts.push(parseVersion2_0(buffer));
if (versionMinor >= 4) {
parts.push(parseVersion2_4(buffer));
}
if (versionMinor >= 5) {
parts.push(parseVersion2_5(buffer));
}
complete(parts);
break;
}
}
private function parseVersion2_0(buffer:ProtocolBuffer):Object {
var codec:ICodec = protocol.getCodec(new TypeCodecInfo(A3D2, false));
var a3d:A3D2 = A3D2(codec.decode(buffer));
return a3d;
}
private function parseVersion2_5(buffer:ProtocolBuffer):Object {
var codec:ICodec = protocol.getCodec(new TypeCodecInfo(A3D2Extra2, false));
var a3d:A3D2Extra2 = A3D2Extra2(codec.decode(buffer));
return a3d;
}
private function parseVersion2_4(buffer:ProtocolBuffer):Object {
var codec:ICodec = protocol.getCodec(new TypeCodecInfo(A3D2Extra1, false));
var a3d:A3D2Extra1 = A3D2Extra1(codec.decode(buffer));
return a3d;
}
}
}
import alternativa.osgi.service.clientlog.IClientLog;
import alternativa.osgi.service.clientlog.IClientLogChannelListener;
class DummyClientLog implements IClientLog {
public function logError(channelName:String, text:String, ...vars):void {
}
public function log(channelName:String, text:String, ...rest):void {
}
public function getChannelStrings(channelName:String):Vector.<String> {
return null;
}
public function addLogListener(listener:IClientLogChannelListener):void {
}
public function removeLogListener(listener:IClientLogChannelListener):void {
}
public function addLogChannelListener(channelName:String, listener:IClientLogChannelListener):void {
}
public function removeLogChannelListener(channelName:String, listener:IClientLogChannelListener):void {
}
public function getChannelNames():Vector.<String> {
return null;
}
}

View File

@@ -0,0 +1,391 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.animation.AnimationClip;
import alternativa.engine3d.animation.keys.Track;
import alternativa.engine3d.core.Light3D;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.loaders.collada.DaeDocument;
import alternativa.engine3d.loaders.collada.DaeMaterial;
import alternativa.engine3d.loaders.collada.DaeNode;
import alternativa.engine3d.loaders.collada.DaeObject;
import alternativa.engine3d.resources.ExternalTextureResource;
use namespace alternativa3d;
/**
* A parser performs parsing of collada xml.
*/
public class ParserCollada extends Parser {
/**
* List of the light sources
*/
public var lights:Vector.<Light3D>;
/**
* Creates a new ParserCollada instance.
*/
public function ParserCollada() {
}
/**
* Clears all links to external objects.
*/
override public function clean():void {
super.clean();
lights = null;
}
/**
* @private
* Initialization before the parsing
*/
override alternativa3d function init():void {
super.init();
// cameras = new Vector.<Camera3D>();
lights = new Vector.<Light3D>();
}
/**
* Method parses <code>xml</code> of collada and fills arrays <code>objects</code>, <code>hierarchy</code>, <code>materials</code>, <code>animations</code>
* If you need to download textures, use class <code>TexturesLoader</code>.
* <p>Path to collada file should match with <code>URI</code> specification. E.g., <code>file:///C:/test.dae</code> or <code>/C:/test.dae</code> for the full paths and <code>test.dae</code>, <code>./test.dae</code> in case of relative.</p>
*
* @param data <code>XML</code> data type of collada.
* @param baseURL Path to textures relative to swf-file (Or file name only in case of <code>trimPaths=true</code>).
* @param trimPaths Use file names only, without paths.
*
* @see alternativa.engine3d.loaders.TexturesLoader
* @see #objects
* @see #hierarchy
* @see #materials
*/
public function parse(data:XML, baseURL:String = null, trimPaths:Boolean = false):void {
init();
var document:DaeDocument = new DaeDocument(data, 0);
if (document.scene != null) {
parseNodes(document.scene.nodes, null, false);
parseMaterials(document.materials, baseURL, trimPaths);
}
}
/**
* Adds components of animated object to lists objects, parents, hierarchy, cameras, animations and to parent container.
*/
private function addObject(animatedObject:DaeObject, parent:Object3D, layer:String):Object3D {
var object:Object3D = Object3D(animatedObject.object);
this.objects.push(object);
if (parent == null) {
this.hierarchy.push(object);
} else {
parent.addChild(object);
}
// if (object is Camera3D) {
// this.cameras.push(object as Camera3D);
// }
if (object is Light3D) {
lights.push(Light3D(object));
}
if (animatedObject.animation != null) {
this.animations.push(animatedObject.animation);
}
if (layer) {
layersMap[object] = layer;
}
return object;
}
/**
* Adds objects to objects, parents, hierarchy, cameras and animations lists and to parent container.
*
* @return first object
*/
private function addObjects(animatedObjects:Vector.<DaeObject>, parent:Object3D, layer:String):Object3D {
var first:Object3D = addObject(animatedObjects[0], parent, layer);
for (var i:int = 1, count:int = animatedObjects.length; i < count; i++) {
addObject(animatedObjects[i], parent, layer);
}
return first;
}
/**
* Check if is there a skin among child objects.
*/
private function hasSkinsInChildren(node:DaeNode):Boolean {
var nodes:Vector.<DaeNode> = node.nodes;
for (var i:int = 0, count:int = nodes.length; i < count; i++) {
var child:DaeNode = nodes[i];
child.parse();
if (child.skins != null) {
return true;
}
if (hasSkinsInChildren(child)) {
return true;
}
}
return false;
}
private function parseNodes(nodes:Vector.<DaeNode>, parent:Object3D, skinsOnly:Boolean = false):void {
for (var i:int = 0, count:int = nodes.length; i < count; i++) {
var node:DaeNode = nodes[i];
node.parse();
// Object to which child objects will be added.
var container:Object3D = null;
if (node.skins != null) {
// Main joint of skin
container = addObjects(node.skins, parent, node.layer);
} else {
if (!skinsOnly && !node.skinOrTopmostJoint) {
if (node.objects != null) {
container = addObjects(node.objects, parent, node.layer);
} else {
// Empty Object3D
container = new Object3D();
container.name = node.cloneString(node.name);
addObject(node.applyAnimation(node.applyTransformations(container)), parent, node.layer);
container.calculateBoundBox();
}
} else {
// Object or its parent is a skin or joint
// Create an object only if there are a child skins
if (hasSkinsInChildren(node)) {
container = new Object3D();
container.name = node.cloneString(node.name);
addObject(node.applyAnimation(node.applyTransformations(container)), parent, node.layer);
parseNodes(node.nodes, container, skinsOnly || node.skinOrTopmostJoint);
container.calculateBoundBox();
}
}
}
// Parse children
if (container != null) {
parseNodes(node.nodes, container, skinsOnly || node.skinOrTopmostJoint);
}
}
}
private function trimPath(path:String):String {
var index:int = path.lastIndexOf("/");
return (index < 0) ? path : path.substr(index + 1);
}
private function parseMaterials(materials:Object, baseURL:String, trimPaths:Boolean):void {
var tmaterial:ParserMaterial;
for each (var material:DaeMaterial in materials) {
if (material.used) {
material.parse();
this.materials.push(material.material);
}
}
var resource:ExternalTextureResource;
// Prepare paths
if (trimPaths) {
for each (tmaterial in this.materials) {
for each(resource in tmaterial.textures) {
if (resource != null && resource.url != null) {
resource.url = trimPath(fixURL(resource.url));
}
}
}
} else {
for each (tmaterial in this.materials) {
for each(resource in tmaterial.textures) {
if (resource != null && resource.url != null) {
resource.url = fixURL(resource.url);
}
}
}
}
var base:String;
if (baseURL != null) {
baseURL = fixURL(baseURL);
var end:int = baseURL.lastIndexOf("/");
base = (end < 0) ? "" : baseURL.substr(0, end);
for each (tmaterial in this.materials) {
for each(resource in tmaterial.textures) {
if (resource != null && resource.url != null) {
resource.url = resolveURL(resource.url, base);
}
}
}
}
}
/**
* @private
* Prosesses URL with following actions.
* Replaces backslashes with slashes, adds three direct slashes after <code>file:</code>
*/
private function fixURL(url:String):String {
var pathStart:int = url.indexOf("://");
pathStart = (pathStart < 0) ? 0 : pathStart + 3;
var pathEnd:int = url.indexOf("?", pathStart);
pathEnd = (pathEnd < 0) ? url.indexOf("#", pathStart) : pathEnd;
var path:String = url.substring(pathStart, (pathEnd < 0) ? 0x7FFFFFFF : pathEnd);
path = path.replace(/\\/g, "/");
var fileIndex:int = url.indexOf("file://");
if (fileIndex >= 0) {
if (url.charAt(pathStart) == "/") {
return "file://" + path + ((pathEnd >= 0) ? url.substring(pathEnd) : "");
}
return "file:///" + path + ((pathEnd >= 0) ? url.substring(pathEnd) : "");
}
return url.substring(0, pathStart) + path + ((pathEnd >= 0) ? url.substring(pathEnd) : "");
}
/**
* @private
*/
private function mergePath(path:String, base:String, relative:Boolean = false):String {
var baseParts:Array = base.split("/");
var parts:Array = path.split("/");
for (var i:int = 0, count:int = parts.length; i < count; i++) {
var part:String = parts[i];
if (part == "..") {
var basePart:String = baseParts.pop();
while (basePart == "." || basePart == "" && basePart != null) basePart = baseParts.pop();
if (relative) {
if (basePart == "..") {
baseParts.push("..", "..");
} else if (basePart == null) {
baseParts.push("..");
}
}
} else {
baseParts.push(part);
}
}
return baseParts.join("/");
}
/**
* @private
* Converts relative paths to full paths
*/
private function resolveURL(url:String, base:String):String {
if (base == "") {
return url;
}
// http://labs.apache.org/webarch/uri/rfc/rfc3986.html
if (url.charAt(0) == "." && url.charAt(1) == "/") {
// File at the same folder
return base + url.substr(1);
} else if (url.charAt(0) == "/") {
// Full path
return url;
} else if (url.charAt(0) == "." && url.charAt(1) == ".") {
// Above on level
var queryAndFragmentIndex:int = url.indexOf("?");
queryAndFragmentIndex = (queryAndFragmentIndex < 0) ? url.indexOf("#") : queryAndFragmentIndex;
var path:String;
var queryAndFragment:String;
if (queryAndFragmentIndex < 0) {
queryAndFragment = "";
path = url;
} else {
queryAndFragment = url.substring(queryAndFragmentIndex);
path = url.substring(0, queryAndFragmentIndex);
}
// Split base URL on parts
var bPath:String;
var bSlashIndex:int = base.indexOf("/");
var bShemeIndex:int = base.indexOf(":");
var bAuthorityIndex:int = base.indexOf("//");
if (bAuthorityIndex < 0 || bAuthorityIndex > bSlashIndex) {
if (bShemeIndex >= 0 && bShemeIndex < bSlashIndex) {
// Scheme exists, no domain
var bSheme:String = base.substring(0, bShemeIndex + 1);
bPath = base.substring(bShemeIndex + 1);
if (bPath.charAt(0) == "/") {
return bSheme + "/" + mergePath(path, bPath.substring(1), false) + queryAndFragment;
} else {
return bSheme + mergePath(path, bPath, false) + queryAndFragment;
}
} else {
// No Scheme, no domain
if (base.charAt(0) == "/") {
return "/" + mergePath(path, base.substring(1), false) + queryAndFragment;
} else {
return mergePath(path, base, true) + queryAndFragment;
}
}
} else {
bSlashIndex = base.indexOf("/", bAuthorityIndex + 2);
var bAuthority:String;
if (bSlashIndex >= 0) {
bAuthority = base.substring(0, bSlashIndex + 1);
bPath = base.substring(bSlashIndex + 1);
return bAuthority + mergePath(path, bPath, false) + queryAndFragment;
} else {
bAuthority = base;
return bAuthority + "/" + mergePath(path, "", false);
}
}
}
var shemeIndex:int = url.indexOf(":");
var slashIndex:int = url.indexOf("/");
if (shemeIndex >= 0 && (shemeIndex < slashIndex || slashIndex < 0)) {
// Contains the schema
return url;
}
return base + "/" + url;
}
/**
* Returns animation from <code>animations</code> array by object, to which it refers.
*/
public function getAnimationByObject(object:Object):AnimationClip {
for each (var animation:AnimationClip in animations) {
var objects:Array = animation._objects;
if (objects.indexOf(object) >= 0) {
return animation;
}
}
return null;
}
/**
* Parses and returns animation.
*/
public static function parseAnimation(data:XML):AnimationClip {
var document:DaeDocument = new DaeDocument(data, 0);
var clip:AnimationClip = new AnimationClip();
collectAnimation(clip, document.scene.nodes);
return (clip.numTracks > 0) ? clip : null;
}
/**
* @private
*/
private static function collectAnimation(clip:AnimationClip, nodes:Vector.<DaeNode>):void {
for (var i:int = 0, count:int = nodes.length; i < count; i++) {
var node:DaeNode = nodes[i];
var animation:AnimationClip = node.parseAnimation();
if (animation != null) {
for (var t:int = 0, numTracks:int = animation.numTracks; t < numTracks; t++) {
var track:Track = animation.getTrackAt(t);
clip.addTrack(track);
}
} else {
clip.addTrack(node.createStaticTransformTrack());
}
collectAnimation(clip, node.nodes);
}
}
}
}

View File

@@ -0,0 +1,107 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Light3D;
import alternativa.engine3d.materials.*;
import alternativa.engine3d.objects.Surface;
import alternativa.engine3d.resources.ExternalTextureResource;
import alternativa.engine3d.resources.Geometry;
import alternativa.engine3d.resources.TextureResource;
import avmplus.getQualifiedClassName;
import flash.utils.Dictionary;
import flash.utils.getDefinitionByName;
use namespace alternativa3d;
/**
* A material which is assigned to each object that we got as a parsing result. This material should be treated as a debugging rather than a production one.
* It keeps links to all possible in Alternativa3D maps (such as light map or normal map) but can render only one of them as a diffuse like
* <code>TextureMaterial</code>. To make object that you get after parsing using all these maps you should create a new <code>StandardMaterial</code>
* and pass to them all textures. Then you can assign this material to the object.
* Since <code>ParserMaterial</code> sores only links to textures, you should worry about loading it. You can use <code>TexturesLoader</code> for.
* Can draws a Skin with no more than 41 Joints per surface. See Skin.divide() for more details.
*
* @see alternativa.engine3d.loaders.TexturesLoader
* @see alternativa.engine3d.materials
* @see alternativa.engine3d.objects.Skin#divide()
*/
public class ParserMaterial extends Material {
/**
* List of colors, that can be assigned to each channel instead of texture. Variants: ambient, emission, diffuse, specular, shininess, reflective, transparent, bump.
*/
public var colors:Object;
/**
* List of <code>ExternalTextureResource</code>, that you can load with a <code>TexturesLoader</code>. Keys of objects are names of channels. Variants: ambient, emission, diffuse, specular, shininess, reflective, transparent, bump.
*
* @see alternativa.engine3d.loaders.TexturesLoader
* @see alternativa.engine3d.resources.ExternalTextureResource
*/
public var textures:Object;
/**
* Transparency of material
*/
public var transparency:Number = 0;
/**
* Channel, that will be rendered. Possible options: ambient, emission, diffuse, specular, shininess, reflective, transparent, bump.
*/
public var renderChannel:String = "diffuse";
private var textureMaterial:TextureMaterial;
private var fillMaterial:FillMaterial;
public function ParserMaterial() {
textures = new Object();
colors = new Object();
}
/**
* @private
*/
override alternativa3d function fillResources(resources:Dictionary, resourceType:Class):void {
super.fillResources(resources, resourceType);
for each(var texture:TextureResource in textures) {
if (texture != null && A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(texture)) as Class, resourceType)) {
resources[texture] = true;
}
}
}
/**
* @private
*/
override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector.<Light3D>, lightsLength:int, objectRenderPriority:int = -1):void {
var colorO:Object = colors[renderChannel];
var map:ExternalTextureResource;
if (colorO != null) {
if(fillMaterial == null) {
fillMaterial = new FillMaterial(int(colorO));
} else {
fillMaterial.color = int(colorO);
}
fillMaterial.collectDraws(camera, surface, geometry, lights, lightsLength, objectRenderPriority);
} else if ((map = textures[renderChannel]) != null) {
if(textureMaterial == null) {
textureMaterial = new TextureMaterial(map);
} else {
textureMaterial.diffuseMap = map;
}
textureMaterial.collectDraws(camera, surface, geometry, lights, lightsLength, objectRenderPriority);
}
}
}
}

View File

@@ -0,0 +1,186 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.resources.BitmapTextureResource;
import alternativa.engine3d.resources.ExternalTextureResource;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display3D.Context3D;
import flash.display3D.Context3DTextureFormat;
import flash.display3D.textures.CubeTexture;
import flash.display3D.textures.Texture;
import flash.display3D.textures.TextureBase;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.geom.Matrix;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import flash.utils.Endian;
use namespace alternativa3d;
/**
* @private
*/
public class ResourceLoader extends EventDispatcher {
private var loadSequence:Vector.<ExternalTextureResource> = new Vector.<ExternalTextureResource>();
private var context:Context3D;
private var currentIndex:int = 0;
public var generateMips:Boolean = true;
private static const atfRegExp:RegExp = new RegExp(/\.atf/i);
public function ResourceLoader(generateMips:Boolean = true) {
this.generateMips = generateMips;
}
public function addResource(resource:ExternalTextureResource):void {
loadSequence.push(resource);
}
public function addResources(resources:Vector.<ExternalTextureResource>):void {
for each (var resource:ExternalTextureResource in resources) {
loadSequence.push(resource);
}
}
public function load(context:Context3D):void {
this.context = context;
currentIndex = 0;
loadNext();
}
private function loadNext():void {
if (currentIndex < loadSequence.length) {
var currentResource:ExternalTextureResource = loadSequence[currentIndex];
if (currentResource.url.match(atfRegExp)) {
var atfLoader:URLLoader = new URLLoader();
atfLoader.dataFormat = URLLoaderDataFormat.BINARY;
atfLoader.addEventListener(Event.COMPLETE, onATFComplete);
atfLoader.addEventListener(IOErrorEvent.IO_ERROR, onFailed);
atfLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onFailed);
atfLoader.load(new URLRequest(currentResource.url));
} else {
var bitmapLoader:Loader = new Loader();
bitmapLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onBitmapLoaded);
bitmapLoader.contentLoaderInfo.addEventListener(IOErrorEvent.DISK_ERROR, onFailed);
bitmapLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onFailed);
bitmapLoader.contentLoaderInfo.addEventListener(IOErrorEvent.NETWORK_ERROR, onFailed);
bitmapLoader.contentLoaderInfo.addEventListener(IOErrorEvent.VERIFY_ERROR, onFailed);
bitmapLoader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onFailed);
bitmapLoader.load(new URLRequest(currentResource.url));
}
} else {
dispatchEvent(new Event(Event.COMPLETE));
}
}
private function getNearPowerOf2For(size:Number):Number {
if (size && (size - 1)) {
for (var i:int = 11; i > 0; i--) {
if (size >= (1 << i)) {
return 1 << i;
}
}
return 0;
} else {
return size;
}
}
private function fitTextureToSizeLimits(textureData:BitmapData):BitmapData {
var fittedTextureData:BitmapData = textureData;
var width:Number, height:Number;
width = getNearPowerOf2For(fittedTextureData.width);
height = getNearPowerOf2For(fittedTextureData.height);
if (width != fittedTextureData.width || height != fittedTextureData.height) {
var newBitmap:BitmapData = new BitmapData(width, height,
fittedTextureData.transparent);
var matrix:Matrix = new Matrix(width/fittedTextureData.width, 0, 0,
height/fittedTextureData.height);
newBitmap.draw(fittedTextureData, matrix);
fittedTextureData = newBitmap;
}
return fittedTextureData;
}
private function onBitmapLoaded(e:Event):void {
var resized:BitmapData = fitTextureToSizeLimits(e.target.content.bitmapData);
var texture:Texture = context.createTexture(resized.width, resized.height, Context3DTextureFormat.BGRA, false);
texture.uploadFromBitmapData(resized, 0);
if (generateMips) {
BitmapTextureResource.createMips(texture, resized);
}
var currentResource:ExternalTextureResource = loadSequence[currentIndex];
currentResource.data = texture;
dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS));
currentIndex++;
loadNext();
}
private function onFailed(e:Event):void {
trace("Failed to load texture :", loadSequence[currentIndex].url);
//dispatchEvent(e);
currentIndex++;
loadNext();
}
private function onATFComplete(e:Event):void {
var value:ByteArray = e.target.data;
value.endian = Endian.LITTLE_ENDIAN;
value.position = 6;
var texture:TextureBase
var type:uint = value.readByte();
var format:String;
switch (type & 0x7F) {
case 0:
format = Context3DTextureFormat.BGRA;
break;
case 1:
format = Context3DTextureFormat.BGRA;
break;
case 2:
format = Context3DTextureFormat.COMPRESSED;
break;
}
if ((type & ~0x7F) == 0) {
texture = context.createTexture(1 << value.readByte(), 1 << value.readByte(), format, false);
texture.addEventListener("textureReady", onTextureUploaded);
Texture(texture).uploadCompressedTextureFromByteArray(value, 0, true);
} else {
texture = context.createCubeTexture(1 << value.readByte(), format, false);
texture.addEventListener("textureReady", onTextureUploaded);
CubeTexture(texture).uploadCompressedTextureFromByteArray(value, 0, true)
}
}
private function onTextureUploaded(e:Event):void {
var currentResource:ExternalTextureResource = loadSequence[currentIndex];
currentResource.data = TextureBase(e.target);
dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS));
currentIndex++;
loadNext();
}
}
}

View File

@@ -0,0 +1,328 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.loaders.events.TexturesLoaderEvent;
import alternativa.engine3d.resources.BitmapTextureResource;
import alternativa.engine3d.resources.ExternalTextureResource;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display3D.Context3D;
import flash.display3D.Context3DTextureFormat;
import flash.display3D.textures.CubeTexture;
import flash.display3D.textures.Texture;
import flash.display3D.textures.TextureBase;
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import flash.utils.Endian;
use namespace alternativa3d;
/**
* Dispatches after complete loading of all textures.
* @eventType flash.events.TexturesLoaderEvent.COMPLETE
*/
[Event(name="complete",type="alternativa.engine3d.loaders.events.TexturesLoaderEvent")]
/**
* An object that downloads textures by their reference and upload them into the <code>Context3D</code>
*/
public class TexturesLoader extends EventDispatcher {
/**
* A <code>Context3D</code> to which resources wil be loaded.
*/
public var context:Context3D;
private var textures:Object = new Object();
private var bitmapDatas:Object = new Object();
private var byteArrays:Object = new Object();
private var currentBitmapDatas:Vector.<BitmapData>;
private var currentUrl:String;
private var resources:Vector.<ExternalTextureResource>;
private var counter:int = 0;
private var createTexture3D:Boolean;
private var needBitmapData:Boolean;
private var loaderCompressed:URLLoader;
private var isATF:Boolean;
private var atfRegExp:RegExp = new RegExp(/\.atf/i);
/**
* Creates a new TexturesLoader instance.
* @param context A <code>Context3D</code> to which resources wil be loaded.
*/
public function TexturesLoader(context:Context3D) {
this.context = context;
}
/**
* @private
*/
public function getTexture(url:String):TextureBase {
return textures[url];
}
private function loadCompressed(url:String):void {
loaderCompressed = new URLLoader();
loaderCompressed.dataFormat = URLLoaderDataFormat.BINARY;
loaderCompressed.addEventListener(Event.COMPLETE, loadNext);
loaderCompressed.addEventListener(IOErrorEvent.IO_ERROR, loadNext);
loaderCompressed.addEventListener(SecurityErrorEvent.SECURITY_ERROR, loadNext);
loaderCompressed.load(new URLRequest(url));
}
/**
* Loads a resource.
* @param resource
* @param createTexture3D Create texture on uploading
* @param needBitmapData If <code>true</code>, keeps <code>BitmapData</code> after uploading textures into a context.
*/
public function loadResource(resource:ExternalTextureResource, createTexture3D:Boolean = true, needBitmapData:Boolean = true):void {
if (resources != null) {
throw new Error("Cannot start new load while loading");
}
this.createTexture3D = createTexture3D;
this.needBitmapData = needBitmapData;
resources = Vector.<ExternalTextureResource>([resource]);
currentBitmapDatas = new Vector.<BitmapData>(1);
//currentTextures3D = new Vector.<Texture>(1);
loadNext();
}
/**
* Loads list of textures
* @param resources List of <code>ExternalTextureResource</code> each of them has link to texture file which needs to be downloaded.
* @param createTexture3D Create texture on uploading.
* @param needBitmapData If <code>true</code>, keeps <code>BitmapData</code> after uploading textures into a context.
*/
public function loadResources(resources:Vector.<ExternalTextureResource>, createTexture3D:Boolean = true, needBitmapData:Boolean = true):void {
if (this.resources != null) {
throw new Error("Cannot start new load while loading");
}
this.createTexture3D = createTexture3D;
this.needBitmapData = needBitmapData;
this.resources = resources;
currentBitmapDatas = new Vector.<BitmapData>(resources.length);
loadNext();
}
/**
* Clears links to all data stored in this <code>TexturesLoader</code> instance. (List of downloaded textures)
*/
public function clean():void {
if (resources != null) {
throw new Error("Cannot clean while loading");
}
textures = new Object();
bitmapDatas = new Object();
}
/**
* Clears links to all data stored in this <code>TexturesLoader</code> instance and removes it from the context.
*/
public function cleanAndDispose():void {
if (resources != null) {
throw new Error("Cannot clean while loading");
}
textures = new Object();
for each (var b:BitmapData in bitmapDatas) {
b.dispose();
}
bitmapDatas = new Object();
}
/**
* Removes texture resources from <code>Context3D</code>.
* @param urls List of links to resources, that should be removed.
*/
public function dispose(urls:Vector.<String>):void {
for (var i:int = 0; i < urls.length; i++) {
var url:String = urls[i];
var bmd:BitmapData = bitmapDatas[url] as BitmapData;
//if (bmd) {
delete bitmapDatas[url];
bmd.dispose();
//}
}
}
private function loadNext(e:Event = null):void {
// trace("[NEXT]", e);
var bitmapData:BitmapData;
var byteArray:ByteArray;
var texture3D:TextureBase;
if (e != null && !(e is ErrorEvent)) {
if (isATF) {
byteArray = e.target.data;
byteArrays[currentUrl] = byteArray;
try {
texture3D = addCompressedTexture(byteArray);
resources[counter - 1].data = texture3D;
} catch (err:Error) {
// throw new Error("loadNext:: " + err.message + " " + currentUrl);
trace("loadNext:: " + err.message + " " + currentUrl);
}
} else {
bitmapData = e.target.content.bitmapData;
bitmapDatas[currentUrl] = bitmapData;
currentBitmapDatas[counter - 1] = bitmapData;
if (createTexture3D) {
try {
texture3D = addTexture(bitmapData);
resources[counter - 1].data = texture3D;
} catch (err:Error) {
throw new Error("loadNext:: " + err.message + " " + currentUrl);
}
}
if (!needBitmapData) {
bitmapData.dispose();
}
}
resources[counter - 1].data = texture3D;
} else if (e is ErrorEvent) {
trace("Missing: " + currentUrl);
}
if (counter < resources.length) {
currentUrl = resources[counter++].url;
if (currentUrl != null) {
atfRegExp.lastIndex = currentUrl.lastIndexOf(".");
isATF = currentUrl.match(atfRegExp) != null;
}
if (isATF) {
if (createTexture3D) {
texture3D = textures[currentUrl];
if (texture3D == null) {
byteArray = byteArrays[currentUrl];
if (byteArray) {
texture3D = addCompressedTexture(byteArray);
resources[counter - 1].data = texture3D;
//bitmapDatas[currentUrl] = bitmapData;
loadNext();
} else {
loadCompressed(currentUrl);
}
} else {
resources[counter - 1].data = texture3D;
loadNext();
}
}
} else {
if (needBitmapData) {
bitmapData = bitmapDatas[currentUrl];
if (bitmapData) {
currentBitmapDatas[counter - 1] = bitmapData;
if (createTexture3D) {
texture3D = textures[currentUrl];
if (texture3D == null) {
texture3D = addTexture(bitmapData);
}
resources[counter - 1].data = texture3D;
}
loadNext();
} else {
load(currentUrl);
}
} else if (createTexture3D) {
texture3D = textures[currentUrl];
if (texture3D == null) {
bitmapData = bitmapDatas[currentUrl];
if (bitmapData) {
texture3D = addTexture(bitmapData);
resources[counter - 1].data = texture3D;
loadNext();
} else {
load(currentUrl);
}
} else {
resources[counter - 1].data = texture3D;
loadNext();
}
}
}
} else {
onTexturesLoad();
}
}
private function load(url:String):void {
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadNext);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.DISK_ERROR, loadNext);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadNext);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.NETWORK_ERROR, loadNext);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.VERIFY_ERROR, loadNext);
loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, loadNext);
loader.load(new URLRequest(url));
}
private function onTexturesLoad():void {
// trace("[LOADED]");
counter = 0;
var bmds:Vector.<BitmapData> = currentBitmapDatas;
var reses:Vector.<ExternalTextureResource> = resources;
currentBitmapDatas = null;
resources = null;
dispatchEvent(new TexturesLoaderEvent(TexturesLoaderEvent.COMPLETE, bmds, reses));
}
private function addTexture(value:BitmapData):Texture {
var texture:Texture = context.createTexture(value.width, value.height, Context3DTextureFormat.BGRA, false);
texture.uploadFromBitmapData(value, 0);
BitmapTextureResource.createMips(texture, value);
textures[currentUrl] = texture;
return texture;
}
private function addCompressedTexture(value:ByteArray):TextureBase {
value.endian = Endian.LITTLE_ENDIAN;
value.position = 6;
var texture:TextureBase
var type:uint = value.readByte();
var format:String;
switch (type & 0x7F) {
case 0:
format = Context3DTextureFormat.BGRA;
break;
case 1:
format = Context3DTextureFormat.BGRA;
break;
case 2:
format = Context3DTextureFormat.COMPRESSED;
break;
}
if ((type & ~0x7F) == 0) {
texture = context.createTexture(1 << value.readByte(), 1 << value.readByte(), format, false);
Texture(texture).uploadCompressedTextureFromByteArray(value, 0);
} else {
texture = context.createCubeTexture(1 << value.readByte(), format, false);
CubeTexture(texture).uploadCompressedTextureFromByteArray(value, 0)
}
textures[currentUrl] = texture;
return texture;
}
}
}

View File

@@ -0,0 +1,51 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
/**
* @private
*/
public class DaeArray extends DaeElement {
use namespace collada;
/**
* Array of String values.
* Call <code>parse()</code> before using.
*/
public var array:Array;
public function DaeArray(data:XML, document:DaeDocument) {
super(data, document);
}
public function get type():String {
return String(data.localName());
}
override protected function parseImplementation():Boolean {
array = parseStringArray(data);
var countXML:XML = data.@count[0];
if (countXML != null) {
var count:int = parseInt(countXML.toString(), 10);
if (array.length < count) {
document.logger.logNotEnoughDataError(data.@count[0]);
return false;
} else {
array.length = count;
return true;
}
}
return false;
}
}
}

View File

@@ -0,0 +1,213 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.animation.keys.NumberKey;
import alternativa.engine3d.animation.keys.NumberTrack;
import alternativa.engine3d.animation.keys.Track;
use namespace alternativa3d;
/**
* @private
*/
public class DaeChannel extends DaeElement {
static public const PARAM_UNDEFINED:String = "undefined";
static public const PARAM_TRANSLATE_X:String = "x";
static public const PARAM_TRANSLATE_Y:String = "y";
static public const PARAM_TRANSLATE_Z:String = "z";
static public const PARAM_SCALE_X:String = "scaleX";
static public const PARAM_SCALE_Y:String = "scaleY";
static public const PARAM_SCALE_Z:String = "scaleZ";
static public const PARAM_ROTATION_X:String = "rotationX";
static public const PARAM_ROTATION_Y:String = "rotationY";
static public const PARAM_ROTATION_Z:String = "rotationZ";
static public const PARAM_TRANSLATE:String = "translate";
static public const PARAM_SCALE:String = "scale";
static public const PARAM_MATRIX:String = "matrix";
/**
* Animation track with keys.
* Call <code>parse()</code> before using.
*/
public var tracks:Vector.<Track>;
/**
* Type of animated parameter. It can be one of DaeChannel.PARAM_*. values.
* * Call <code>parse()</code> before using.
*/
public var animatedParam:String = PARAM_UNDEFINED;
/**
* Key of animated object.
*/
public var animName:String;
public function DaeChannel(data:XML, document:DaeDocument) {
super(data, document);
}
/**
* Returns a node for which the animation is destined.
*/
public function get node():DaeNode {
var targetXML:XML = data.@target[0];
if (targetXML != null) {
var targetParts:Array = targetXML.toString().split("/");
// First part of item id
var node:DaeNode = document.findNodeByID(targetParts[0]);
if (node != null) {
// Last part is transformed item
targetParts.pop();
for (var i:int = 1, count:int = targetParts.length; i < count; i++) {
var sid:String = targetParts[i];
node = node.getNodeBySid(sid);
if (node == null) {
return null;
}
}
return node;
}
}
return null;
}
override protected function parseImplementation():Boolean {
parseTransformationType();
parseSampler();
return true;
}
private function parseTransformationType():void {
var targetXML:XML = data.@target[0];
if (targetXML == null) return;
// Split the path on parts
var targetParts:Array = targetXML.toString().split("/");
var sid:String = targetParts.pop();
var sidParts:Array = sid.split(".");
var sidPartsCount:int = sidParts.length;
//Define the type of property
var transformationXML:XML;
var node:DaeNode = this.node;
if (node == null) {
return;
}
animName = node.animName;
var children:XMLList = node.data.children();
for (var i:int = 0, count:int = children.length(); i < count; i++) {
var child:XML = children[i];
var attr:XML = child.@sid[0];
if (attr != null && attr.toString() == sidParts[0]) {
transformationXML = child;
break;
}
}
// TODO:: case with brackets (just in case)
var transformationName:String = (transformationXML != null) ? transformationXML.localName() as String : null;
if (sidPartsCount > 1) {
var componentName:String = sidParts[1];
switch (transformationName) {
case "translate":
switch (componentName) {
case "X":
animatedParam = PARAM_TRANSLATE_X;
break;
case "Y":
animatedParam = PARAM_TRANSLATE_Y;
break;
case "Z":
animatedParam = PARAM_TRANSLATE_Z;
break;
}
break;
case "rotate": {
var axis:Array = parseNumbersArray(transformationXML);
// TODO:: look for the maximum value
switch (axis.indexOf(1)) {
case 0:
animatedParam = PARAM_ROTATION_X;
break;
case 1:
animatedParam = PARAM_ROTATION_Y;
break;
case 2:
animatedParam = PARAM_ROTATION_Z;
break;
}
break;
}
case "scale":
switch (componentName) {
case "X":
animatedParam = PARAM_SCALE_X;
break;
case "Y":
animatedParam = PARAM_SCALE_Y;
break;
case "Z":
animatedParam = PARAM_SCALE_Z;
break;
}
break;
}
} else {
switch (transformationName) {
case "translate":
animatedParam = PARAM_TRANSLATE;
break;
case "scale":
animatedParam = PARAM_SCALE;
break;
case "matrix":
animatedParam = PARAM_MATRIX;
break;
}
}
}
private function parseSampler():void {
var sampler:DaeSampler = document.findSampler(data.@source[0]);
if (sampler != null) {
sampler.parse();
if (animatedParam == PARAM_MATRIX) {
tracks = Vector.<Track>([sampler.parseTransformationTrack(animName)]);
return;
}
if (animatedParam == PARAM_TRANSLATE) {
tracks = sampler.parsePointsTracks(animName, "x", "y", "z");
return;
}
if (animatedParam == PARAM_SCALE) {
tracks = sampler.parsePointsTracks(animName, "scaleX", "scaleY", "scaleZ");
return;
}
if (animatedParam == PARAM_ROTATION_X || animatedParam == PARAM_ROTATION_Y || animatedParam == PARAM_ROTATION_Z) {
var track:NumberTrack = sampler.parseNumbersTrack(animName, animatedParam);
// Convert degrees to radians
var toRad:Number = Math.PI/180;
for (var key:NumberKey = track.keyList; key != null; key = key.next) {
key._value *= toRad;
}
tracks = Vector.<Track>([track]);
return;
}
if (animatedParam == PARAM_TRANSLATE_X || animatedParam == PARAM_TRANSLATE_Y || animatedParam == PARAM_TRANSLATE_Z || animatedParam == PARAM_SCALE_X || animatedParam == PARAM_SCALE_Y || animatedParam == PARAM_SCALE_Z) {
tracks = Vector.<Track>([sampler.parseNumbersTrack(animName, animatedParam)]);
}
} else {
document.logger.logNotFoundError(data.@source[0]);
}
}
}
}

View File

@@ -0,0 +1,542 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.*;
import alternativa.engine3d.animation.AnimationClip;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.VertexAttributes;
import alternativa.engine3d.loaders.ParserMaterial;
import alternativa.engine3d.objects.Joint;
import alternativa.engine3d.objects.Skin;
import alternativa.engine3d.resources.Geometry;
import flash.utils.ByteArray;
import flash.utils.Dictionary;
import flash.utils.Endian;
use namespace collada;
use namespace alternativa3d;
/**
* @private
*/
public class DaeController extends DaeElement {
private var jointsBindMatrices:Vector.<Vector.<Number> >;
private var vcounts:Array;
private var indices:Array;
private var jointsInput:DaeInput;
private var weightsInput:DaeInput;
private var inputsStride:int;
private var geometry:Geometry;
private var primitives:Vector.<DaePrimitive>;
private var maxJointsPerVertex:int = 0;
private var bindShapeMatrix:Vector.<Number>;
public function DaeController(data:XML, document:DaeDocument) {
super(data, document);
// sources creates inside the DaeDocument. It should not be here.
}
private function get daeGeometry():DaeGeometry {
var geom:DaeGeometry = document.findGeometry(data.skin.@source[0]);
if (geom == null) {
document.logger.logNotFoundError(data.@source[0]);
}
return geom;
}
override protected function parseImplementation():Boolean {
var vertexWeightsXML:XML = this.data.skin.vertex_weights[0];
if (vertexWeightsXML == null) {
return false;
}
var vcountsXML:XML = vertexWeightsXML.vcount[0];
if (vcountsXML == null) {
return false;
}
vcounts = parseIntsArray(vcountsXML);
var indicesXML:XML = vertexWeightsXML.v[0];
if (indicesXML == null) {
return false;
}
indices = parseIntsArray(indicesXML);
parseInputs();
parseJointsBindMatrices();
var i:int, j:int;
for (i = 0; i < vcounts.length; i++) {
var count:int = vcounts[i];
if (maxJointsPerVertex < count) maxJointsPerVertex = count;
}
var geom:DaeGeometry = this.daeGeometry;
bindShapeMatrix = getBindShapeMatrix();
if (geom != null) {
geom.parse();
var vertices:Vector.<DaeVertex> = geom.geometryVertices;
var source:Geometry = geom.geometry;
var localMaxJointsPerVertex:int = (maxJointsPerVertex%2 != 0) ? maxJointsPerVertex + 1 : maxJointsPerVertex;
// Create geometry
this.geometry = new Geometry();
this.geometry._indices = source._indices.slice();
var attributes:Array = source.getVertexStreamAttributes(0);
var numSourceAttributes:int = attributes.length;
var index:int = numSourceAttributes;
for (i = 0; i < localMaxJointsPerVertex; i += 2) {
var attribute:int = VertexAttributes.JOINTS[int(i/2)];
attributes[int(index++)] = attribute;
attributes[int(index++)] = attribute;
attributes[int(index++)] = attribute;
attributes[int(index++)] = attribute;
}
var numMappings:int = attributes.length;
var sourceData:ByteArray = source._vertexStreams[0].data;
var data:ByteArray = new ByteArray();
data.endian = Endian.LITTLE_ENDIAN;
data.length = 4*numMappings*source._numVertices;
// Copy source data
sourceData.position = 0;
for (i = 0; i < source._numVertices; i++) {
data.position = 4*numMappings*i;
for (j = 0; j < numSourceAttributes; j++) {
data.writeFloat(sourceData.readFloat());
}
}
// Copy weights and joints
var byteArray:ByteArray = createVertexBuffer(vertices, localMaxJointsPerVertex);
byteArray.position = 0;
for (i = 0; i < source._numVertices; i++) {
data.position = 4*(numMappings*i + numSourceAttributes);
for (j = 0; j < localMaxJointsPerVertex; j++) {
data.writeFloat(byteArray.readFloat());
data.writeFloat(byteArray.readFloat());
}
}
this.geometry.addVertexStream(attributes);
this.geometry._vertexStreams[0].data = data;
this.geometry._numVertices = source._numVertices;
transformVertices(this.geometry);
primitives = geom.primitives;
}
return true;
}
private function transformVertices(geometry:Geometry):void {
var data:ByteArray = geometry._vertexStreams[0].data;
var numMappings:int = geometry._vertexStreams[0].attributes.length;
for (var i:int = 0; i < geometry._numVertices; i++) {
data.position = 4*numMappings*i;
var x:Number = data.readFloat();
var y:Number = data.readFloat();
var z:Number = data.readFloat();
data.position -= 12;
data.writeFloat(x*bindShapeMatrix[0] + y*bindShapeMatrix[1] + z*bindShapeMatrix[2] + bindShapeMatrix[3]);
data.writeFloat(x*bindShapeMatrix[4] + y*bindShapeMatrix[5] + z*bindShapeMatrix[6] + bindShapeMatrix[7]);
data.writeFloat(x*bindShapeMatrix[8] + y*bindShapeMatrix[9] + z*bindShapeMatrix[10] + bindShapeMatrix[11]);
}
}
private function createVertexBuffer(vertices:Vector.<DaeVertex>, localMaxJointsPerVertex:int):ByteArray {
var jointsOffset:int = jointsInput.offset;
var weightsOffset:int = weightsInput.offset;
var weightsSource:DaeSource = weightsInput.prepareSource(1);
var weights:Vector.<Number> = weightsSource.numbers;
var weightsStride:int = weightsSource.stride;
var i:int, count:int;
var verticesDict:Dictionary = new Dictionary();
var byteArray:ByteArray = new ByteArray();
// Reserve required number of bytes
byteArray.length = vertices.length*localMaxJointsPerVertex*8;
byteArray.endian = Endian.LITTLE_ENDIAN;
for (i = 0,count = vertices.length; i < count; i++) {
var vertex:DaeVertex = vertices[i];
if (vertex == null) continue;
var vec:Vector.<uint> = verticesDict[vertex.vertexInIndex];
if (vec == null) {
vec = verticesDict[vertex.vertexInIndex] = new Vector.<uint>();
}
vec.push(vertex.vertexOutIndex);
}
var vertexIndex:int = 0;
var vertexOutIndices:Vector.<uint>;
for (i = 0,count = vcounts.length; i < count; i++) {
var jointsPerVertex:int = vcounts[i];
vertexOutIndices = verticesDict[i];
for (var j:int = 0; j < vertexOutIndices.length; j++) {
byteArray.position = vertexOutIndices[j]*localMaxJointsPerVertex*8;
// Loop on joints
for (var k:int = 0; k < jointsPerVertex; k++) {
var index:int = inputsStride*(vertexIndex + k);
var jointIndex:int = indices[int(index + jointsOffset)];
if (jointIndex >= 0) {
// Save joint index, multiplied with three
byteArray.writeFloat(jointIndex*3);
var weightIndex:int = indices[int(index + weightsOffset)];
byteArray.writeFloat(weights[int(weightsStride*weightIndex)]);
} else {
byteArray.position += 8;
}
}
}
vertexIndex += jointsPerVertex;
}
byteArray.position = 0;
return byteArray;
}
private function parseInputs():void {
var inputsList:XMLList = data.skin.vertex_weights.input;
var maxInputOffset:int = 0;
for (var i:int = 0, count:int = inputsList.length(); i < count; i++) {
var input:DaeInput = new DaeInput(inputsList[i], document);
var semantic:String = input.semantic;
if (semantic != null) {
switch (semantic) {
case "JOINT" :
if (jointsInput == null) {
jointsInput = input;
}
break;
case "WEIGHT" :
if (weightsInput == null) {
weightsInput = input;
}
break;
}
}
var offset:int = input.offset;
maxInputOffset = (offset > maxInputOffset) ? offset : maxInputOffset;
}
inputsStride = maxInputOffset + 1;
}
/**
* Parses inverse matrices for joints and saves them to a vector.
*/
private function parseJointsBindMatrices():void {
var jointsXML:XML = data.skin.joints.input.(@semantic == "INV_BIND_MATRIX")[0];
if (jointsXML != null) {
var jointsSource:DaeSource = document.findSource(jointsXML.@source[0]);
if (jointsSource != null) {
if (jointsSource.parse() && jointsSource.numbers != null && jointsSource.stride >= 16) {
var stride:int = jointsSource.stride;
var count:int = jointsSource.numbers.length/stride;
jointsBindMatrices = new Vector.<Vector.<Number> >(count);
for (var i:int = 0; i < count; i++) {
var index:int = stride*i;
var matrix:Vector.<Number> = new Vector.<Number>(16);
jointsBindMatrices[i] = matrix;
for (var j:int = 0; j < 16; j++) {
matrix[j] = jointsSource.numbers[int(index + j)];
}
}
}
} else {
document.logger.logNotFoundError(jointsXML.@source[0]);
}
}
}
/**
* Returns geometry with the joints and controller for the joints.
* Call <code>parse()</code> before using.
*/
public function parseSkin(materials:Object, topmostJoints:Vector.<DaeNode>, skeletons:Vector.<DaeNode>):DaeObject {
var skinXML:XML = data.skin[0];
if (skinXML != null) {
bindShapeMatrix = getBindShapeMatrix();
var numJoints:int = jointsBindMatrices.length;
var skin:Skin = new Skin(maxJointsPerVertex);
skin.geometry = this.geometry;
var joints:Vector.<DaeObject> = addJointsToSkin(skin, topmostJoints, findNodes(skeletons));
setJointsBindMatrices(joints);
skin.renderedJoints = collectRenderedJoints(joints, numJoints);
if (primitives != null) {
for (var i:int = 0; i < primitives.length; i++) {
var p:DaePrimitive = primitives[i];
var instanceMaterial:DaeInstanceMaterial = materials[p.materialSymbol];
var material:ParserMaterial;
if (instanceMaterial != null) {
var daeMaterial:DaeMaterial = instanceMaterial.material;
if (daeMaterial != null) {
daeMaterial.parse();
material = daeMaterial.material;
daeMaterial.used = true;
}
}
skin.addSurface(material, p.indexBegin, p.numTriangles);
}
}
skin.calculateBoundBox();
return new DaeObject(skin, mergeJointsClips(skin, joints));
}
return null;
}
private function collectRenderedJoints(joints:Vector.<DaeObject>, numJoints:int):Vector.<Joint> {
var result:Vector.<Joint> = new Vector.<Joint>();
for (var i:int = 0; i < numJoints; i++) {
result[i] = Joint(joints[i].object);
}
return result;
}
/**
* Unites animations of joints into the single animation, if required.
*/
private function mergeJointsClips(skin:Skin, joints:Vector.<DaeObject>):AnimationClip {
if (!hasJointsAnimation(joints)) {
return null;
}
var result:AnimationClip = new AnimationClip();
var resultObjects:Array = [skin];
for (var i:int = 0, count:int = joints.length; i < count; i++) {
var animatedObject:DaeObject = joints[i];
var clip:AnimationClip = animatedObject.animation;
if (clip != null) {
for (var t:int = 0; t < clip.numTracks; t++) {
result.addTrack(clip.getTrackAt(t));
}
} else {
// Creates empty track for the joint.
result.addTrack(animatedObject.jointNode.createStaticTransformTrack());
}
var object:Object3D = animatedObject.object;
object.name = animatedObject.jointNode.animName;
resultObjects.push(object);
}
result._objects = resultObjects;
return result;
}
private function hasJointsAnimation(joints:Vector.<DaeObject>):Boolean {
for (var i:int = 0, count:int = joints.length; i < count; i++) {
var object:DaeObject = joints[i];
if (object.animation != null) {
return true;
}
}
return false;
}
/**
* Set inverse matrices to joints.
*/
private function setJointsBindMatrices(animatedJoints:Vector.<DaeObject>):void {
for (var i:int = 0, count:int = jointsBindMatrices.length; i < count; i++) {
var animatedJoint:DaeObject = animatedJoints[i];
var bindMatrix:Vector.<Number> = jointsBindMatrices[i];
// bindMatrix[3]; //*= document.unitScaleFactor;
// bindMatrix[7];// *= document.unitScaleFactor;
// bindMatrix[11];// *= document.unitScaleFactor;
Joint(animatedJoint.object).setBindPoseMatrix(bindMatrix);
}
}
/**
* Creates a hierarchy of joints and adds them to skin.
* @return vector of joints with animation, which was added to skin.
* If you have added the auxiliary joint, then length of vector will differ from length of nodes vector.
*/
private function addJointsToSkin(skin:Skin, topmostJoints:Vector.<DaeNode>, nodes:Vector.<DaeNode>):Vector.<DaeObject> {
// Dictionary, in which key is a node and value is a position in nodes vector
var nodesDictionary:Dictionary = new Dictionary();
var count:int = nodes.length;
var i:int;
for (i = 0; i < count; i++) {
nodesDictionary[nodes[i]] = i;
}
var animatedJoints:Vector.<DaeObject> = new Vector.<DaeObject>(count);
var numTopmostJoints:int = topmostJoints.length;
for (i = 0; i < numTopmostJoints; i++) {
var topmostJoint:DaeNode = topmostJoints[i];
var animatedJoint:DaeObject = addRootJointToSkin(skin, topmostJoint, animatedJoints, nodesDictionary);
addJointChildren(Joint(animatedJoint.object), animatedJoints, topmostJoint, nodesDictionary);
}
return animatedJoints;
}
/**
* Adds root joint to skin.
*/
private function addRootJointToSkin(skin:Skin, node:DaeNode, animatedJoints:Vector.<DaeObject>, nodes:Dictionary):DaeObject {
var joint:Joint = new Joint();
joint.name = cloneString(node.name);
skin.addChild(joint);
var animatedJoint:DaeObject = node.applyAnimation(node.applyTransformations(joint));
animatedJoint.jointNode = node;
if (node in nodes) {
animatedJoints[nodes[node]] = animatedJoint;
} else {
// Add at the end
animatedJoints.push(animatedJoint);
}
return animatedJoint;
}
/**
* Creates a hierarchy of child joints and add them to parent joint.
* @param parent Parent joint.
* @param animatedJoints Vector of joints to which created joints will added.
* Auxiliary joints will be added to the end of the vector, if it's necessary.
* @param parentNode Node of parent joint
* @param nodes Dictionary. Key is a node of joint. And value is an index of joint in animatedJoints vector
*
*/
private function addJointChildren(parent:Joint, animatedJoints:Vector.<DaeObject>, parentNode:DaeNode, nodes:Dictionary):void {
var object:DaeObject;
var children:Vector.<DaeNode> = parentNode.nodes;
for (var i:int = 0, count:int = children.length; i < count; i++) {
var child:DaeNode = children[i];
var joint:Joint;
if (child in nodes) {
joint = new Joint();
joint.name = cloneString(child.name);
object = child.applyAnimation(child.applyTransformations(joint));
object.jointNode = child;
animatedJoints[nodes[child]] = object;
parent.addChild(joint);
addJointChildren(joint, animatedJoints, child, nodes);
} else {
// If node is not a joint
if (hasJointInDescendants(child, nodes)) {
// If one of the children is a joint, there is need to create auxiliary joint instead of this node.
joint = new Joint();
joint.name = cloneString(child.name);
object = child.applyAnimation(child.applyTransformations(joint));
object.jointNode = child;
// Add new joint to the end
animatedJoints.push(object);
parent.addChild(joint);
addJointChildren(joint, animatedJoints, child, nodes);
}
}
}
}
private function hasJointInDescendants(parentNode:DaeNode, nodes:Dictionary):Boolean {
var children:Vector.<DaeNode> = parentNode.nodes;
for (var i:int = 0, count:int = children.length; i < count; i++) {
var child:DaeNode = children[i];
if (child in nodes || hasJointInDescendants(child, nodes)) {
return true;
}
}
return false;
}
/**
* Transforms all object vertices with the BindShapeMatrix from collada.
*/
private function getBindShapeMatrix():Vector.<Number> {
var matrixXML:XML = data.skin.bind_shape_matrix[0];
var res:Vector.<Number> = new Vector.<Number>(16, true);
if (matrixXML != null) {
var matrix:Array = parseStringArray(matrixXML);
for (var i:int = 0; i < matrix.length; i++) {
res[i] = Number(matrix[i]);
}
}
return res;
}
/**
* Returns <code>true</code> if joint hasn't parent joint.
* @param node Joint node
* @param nodes Dictionary. It items are the nodes keys.
*
*/
private function isRootJointNode(node:DaeNode, nodes:Dictionary):Boolean {
for (var parent:DaeNode = node.parent; parent != null; parent = parent.parent) {
if (parent in nodes) {
return false;
}
}
return true;
}
public function findRootJointNodes(skeletons:Vector.<DaeNode>):Vector.<DaeNode> {
var nodes:Vector.<DaeNode> = findNodes(skeletons);
var i:int = 0;
var count:int = nodes.length;
if (count > 0) {
var nodesDictionary:Dictionary = new Dictionary();
for (i = 0; i < count; i++) {
nodesDictionary[nodes[i]] = i;
}
var rootNodes:Vector.<DaeNode> = new Vector.<DaeNode>();
for (i = 0; i < count; i++) {
var node:DaeNode = nodes[i];
if (isRootJointNode(node, nodesDictionary)) {
rootNodes.push(node);
}
}
return rootNodes;
}
return null;
}
/**
* Find node by Sid on sceletons vector.
*/
private function findNode(nodeName:String, skeletons:Vector.<DaeNode>):DaeNode {
var count:int = skeletons.length;
for (var i:int = 0; i < count; i++) {
var node:DaeNode = skeletons[i].getNodeBySid(nodeName);
if (node != null) {
return node;
}
}
return null;
}
/**
* Returns the vector of joint nodes.
*/
private function findNodes(skeletons:Vector.<DaeNode>):Vector.<DaeNode> {
var jointsXML:XML = data.skin.joints.input.(@semantic == "JOINT")[0];
if (jointsXML != null) {
var jointsSource:DaeSource = document.findSource(jointsXML.@source[0]);
if (jointsSource != null) {
if (jointsSource.parse() && jointsSource.names != null) {
var stride:int = jointsSource.stride;
var count:int = jointsSource.names.length/stride;
var nodes:Vector.<DaeNode> = new Vector.<DaeNode>(count);
for (var i:int = 0; i < count; i++) {
var node:DaeNode = findNode(jointsSource.names[int(stride*i)], skeletons);
if (node == null) {
// Error: no node.
}
nodes[i] = node;
}
return nodes;
}
} else {
document.logger.logNotFoundError(jointsXML.@source[0]);
}
}
return null;
}
}
}

View File

@@ -0,0 +1,248 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
/**
* @private
*/
public class DaeDocument {
use namespace collada;
public var scene:DaeVisualScene;
/**
* Collada file
*/
private var data:XML;
// Dictionaries to store matchings id-> DaeElement
internal var sources:Object;
internal var arrays:Object;
internal var vertices:Object;
public var geometries:Object;
internal var nodes:Object;
internal var lights:Object;
internal var images:Object;
internal var effects:Object;
internal var controllers:Object;
internal var samplers:Object;
public var materials:Object;
internal var logger:DaeLogger;
public var versionMajor:uint;
public var versionMinor:uint;
public var unitScaleFactor:Number = 1;
public function DaeDocument(document:XML, units:Number) {
this.data = document;
var versionComponents:Array = data.@version[0].toString().split(/[.,]/);
versionMajor = parseInt(versionComponents[1], 10);
versionMinor = parseInt(versionComponents[2], 10);
var colladaUnit:Number = parseFloat(data.asset[0].unit[0].@meter);
if (units > 0) {
unitScaleFactor = colladaUnit/units;
} else {
unitScaleFactor = 1;
}
logger = new DaeLogger();
constructStructures();
constructScenes();
registerInstanceControllers();
constructAnimations();
}
private function getLocalID(url:XML):String {
var path:String = url.toString();
if (path.charAt(0) == "#") {
return path.substr(1);
} else {
logger.logExternalError(url);
return null;
}
}
// Search for the declarations of items and fill the dictionaries.
private function constructStructures():void {
var element:XML;
sources = new Object();
arrays = new Object();
for each (element in data..source) {
// Collect all <source>. Dictionary <code>arrays</code> is filled at constructors.
var source:DaeSource = new DaeSource(element, this);
if (source.id != null) {
sources[source.id] = source;
}
}
lights = new Object();
for each (element in data.library_lights.light) {
// Collect all <image>.
var light:DaeLight = new DaeLight(element, this);
if (light.id != null) {
lights[light.id] = light;
}
}
images = new Object();
for each (element in data.library_images.image) {
// Collect all <image>.
var image:DaeImage = new DaeImage(element, this);
if (image.id != null) {
images[image.id] = image;
}
}
effects = new Object();
for each (element in data.library_effects.effect) {
// Collect all <effect>. Dictionary <code>images</code> is filled at constructors.
var effect:DaeEffect = new DaeEffect(element, this);
if (effect.id != null) {
effects[effect.id] = effect;
}
}
materials = new Object();
for each (element in data.library_materials.material) {
// Collect all <material>.
var material:DaeMaterial = new DaeMaterial(element, this);
if (material.id != null) {
materials[material.id] = material;
}
}
geometries = new Object();
vertices = new Object();
for each (element in data.library_geometries.geometry) {
// Collect all <geometry>. Dictionary <code>vertices</code> is filled at constructors.
var geom:DaeGeometry = new DaeGeometry(element, this);
if (geom.id != null) {
geometries[geom.id] = geom;
}
}
controllers = new Object();
for each (element in data.library_controllers.controller) {
// Collect all <controllers>.
var controller:DaeController = new DaeController(element, this);
if (controller.id != null) {
controllers[controller.id] = controller;
}
}
nodes = new Object();
for each (element in data.library_nodes.node) {
// Create only root nodes. Others are created recursively at constructors.
var node:DaeNode = new DaeNode(element, this);
if (node.id != null) {
nodes[node.id] = node;
}
}
}
private function constructScenes():void {
var vsceneURL:XML = data.scene.instance_visual_scene.@url[0];
var vsceneID:String = getLocalID(vsceneURL);
for each (var element:XML in data.library_visual_scenes.visual_scene) {
// Create visual_scene. node's are created at constructors.
var vscene:DaeVisualScene = new DaeVisualScene(element, this);
if (vscene.id == vsceneID) {
this.scene = vscene;
}
}
if (vsceneID != null && scene == null) {
logger.logNotFoundError(vsceneURL);
}
}
private function registerInstanceControllers():void {
if (scene != null) {
for (var i:int = 0, count:int = scene.nodes.length; i < count; i++) {
scene.nodes[i].registerInstanceControllers();
}
}
}
private function constructAnimations():void {
var element:XML;
samplers = new Object();
for each (element in data.library_animations..sampler) {
// Collect all <sampler>.
var sampler:DaeSampler = new DaeSampler(element, this);
if (sampler.id != null) {
samplers[sampler.id] = sampler;
}
}
for each (element in data.library_animations..channel) {
var channel:DaeChannel = new DaeChannel(element, this);
var node:DaeNode = channel.node;
if (node != null) {
node.addChannel(channel);
}
}
}
public function findArray(url:XML):DaeArray {
return arrays[getLocalID(url)];
}
public function findSource(url:XML):DaeSource {
return sources[getLocalID(url)];
}
public function findLight(url:XML):DaeLight {
return lights[getLocalID(url)];
}
public function findImage(url:XML):DaeImage {
return images[getLocalID(url)];
}
public function findImageByID(id:String):DaeImage {
return images[id];
}
public function findEffect(url:XML):DaeEffect {
return effects[getLocalID(url)];
}
public function findMaterial(url:XML):DaeMaterial {
return materials[getLocalID(url)];
}
public function findVertices(url:XML):DaeVertices {
return vertices[getLocalID(url)];
}
public function findGeometry(url:XML):DaeGeometry {
return geometries[getLocalID(url)];
}
public function findNode(url:XML):DaeNode {
return nodes[getLocalID(url)];
}
public function findNodeByID(id:String):DaeNode {
return nodes[id];
}
public function findController(url:XML):DaeController {
return controllers[getLocalID(url)];
}
public function findSampler(url:XML):DaeSampler {
return samplers[getLocalID(url)];
}
}
}

View File

@@ -0,0 +1,215 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.loaders.ParserMaterial;
import alternativa.engine3d.resources.ExternalTextureResource;
/**
* @private
*/
public class DaeEffect extends DaeElement {
public static var commonAlways:Boolean = false;
use namespace collada;
private var effectParams:Object;
private var commonParams:Object;
private var techniqueParams:Object;
private var diffuse:DaeEffectParam;
private var ambient:DaeEffectParam;
private var transparent:DaeEffectParam;
private var transparency:DaeEffectParam;
private var bump:DaeEffectParam;
private var reflective:DaeEffectParam;
private var emission:DaeEffectParam;
private var specular:DaeEffectParam;
public function DaeEffect(data:XML, document:DaeDocument) {
super(data, document);
// image's are declared at <effect>
constructImages();
}
private function constructImages():void {
var list:XMLList = data..image;
for each (var element:XML in list) {
var image:DaeImage = new DaeImage(element, document);
if (image.id != null) {
document.images[image.id] = image;
}
}
}
override protected function parseImplementation():Boolean {
var element:XML;
var param:DaeParam;
effectParams = new Object();
for each (element in data.newparam) {
param = new DaeParam(element, document);
effectParams[param.sid] = param;
}
commonParams = new Object();
for each (element in data.profile_COMMON.newparam) {
param = new DaeParam(element, document);
commonParams[param.sid] = param;
}
techniqueParams = new Object();
var technique:XML = data.profile_COMMON.technique[0];
if (technique != null) {
for each (element in technique.newparam) {
param = new DaeParam(element, document);
techniqueParams[param.sid] = param;
}
}
var shader:XML = data.profile_COMMON.technique.*.(localName() == "constant" || localName() == "lambert" || localName() == "phong" || localName() == "blinn")[0];
if (shader != null) {
var diffuseXML:XML = null;
if (shader.localName() == "constant") {
diffuseXML = shader.emission[0];
} else {
diffuseXML = shader.diffuse[0];
var emissionXML:XML = shader.emission[0];
if (emissionXML != null) {
emission = new DaeEffectParam(emissionXML, this);
}
}
if (diffuseXML != null) {
diffuse = new DaeEffectParam(diffuseXML, this);
}
if (shader.localName() == "phong" || shader.localName() == "blinn") {
var specularXML:XML = shader.specular[0];
if (specularXML != null) {
specular = new DaeEffectParam(specularXML, this);
}
}
var transparentXML:XML = shader.transparent[0];
if (transparentXML != null) {
transparent = new DaeEffectParam(transparentXML, this);
}
var transparencyXML:XML = shader.transparency[0];
if (transparencyXML != null) {
transparency = new DaeEffectParam(transparencyXML, this);
}
var ambientXML:XML = shader.ambient[0];
if(ambientXML != null) {
ambient = new DaeEffectParam(ambientXML, this);
}
var reflectiveXML:XML = shader.reflective[0];
if(reflectiveXML != null) {
reflective = new DaeEffectParam(reflectiveXML, this);
}
}
var bumpXML:XML = data.profile_COMMON.technique.extra.technique.(hasOwnProperty("@profile") && @profile == "OpenCOLLADA3dsMax").bump[0];
if (bumpXML != null) {
bump = new DaeEffectParam(bumpXML, this);
}
return true;
}
internal function getParam(name:String, setparams:Object):DaeParam {
var param:DaeParam = setparams[name];
if (param != null) {
return param;
}
param = techniqueParams[name];
if (param != null) {
return param;
}
param = commonParams[name];
if (param != null) {
return param;
}
return effectParams[name];
}
private function float4ToUint(value:Array, alpha:Boolean = true):uint {
var r:uint = (value[0] * 255);
var g:uint = (value[1] * 255);
var b:uint = (value[2] * 255);
if (alpha) {
var a:uint = (value[3] * 255);
return (a << 24) | (r << 16) | (g << 8) | b;
} else {
return (r << 16) | (g << 8) | b;
}
}
/**
* Returns material of the engine with given parameters.
* Call <code>parse()</code> before using.
*/
public function getMaterial(setparams:Object):ParserMaterial {
if (diffuse != null) {
var material:ParserMaterial = new ParserMaterial();
if (diffuse) {
pushMap(material, diffuse, setparams);
}
if (specular != null) {
pushMap(material, specular, setparams);
}
if (emission != null) {
pushMap(material, emission, setparams);
}
if (transparency != null) {
material.transparency = transparency.getFloat(setparams);
}
if (transparent != null) {
pushMap(material, transparent, setparams);
}
if (bump != null) {
pushMap(material, bump, setparams);
}
if (ambient) {
pushMap(material, ambient, setparams);
}
if (reflective) {
pushMap(material, reflective, setparams);
}
return material;
}
return null;
}
private function pushMap(material:ParserMaterial, param:DaeEffectParam, setparams:Object):void {
var color:Array = param.getColor(setparams);
if(color != null){
material.colors[cloneString(param.data.localName())] = float4ToUint(color, true);
}
else {
var image:DaeImage = param.getImage(setparams);
if(image != null){
material.textures[cloneString(param.data.localName())] = new ExternalTextureResource(cloneString(image.init_from));
}
}
}
/**
* Name of texture channel for main map of object.
* Call <code>parse()</code> before using.
*/
public function get mainTexCoords():String {
var channel:String = null;
channel = (channel == null && diffuse != null) ? diffuse.texCoord : channel;
channel = (channel == null && transparent != null) ? transparent.texCoord : channel;
channel = (channel == null && bump != null) ? bump.texCoord : channel;
channel = (channel == null && emission != null) ? emission.texCoord : channel;
channel = (channel == null && specular != null) ? specular.texCoord : channel;
return channel;
}
}
}

View File

@@ -0,0 +1,95 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
/**
* @private
*/
public class DaeEffectParam extends DaeElement {
use namespace collada;
private var effect:DaeEffect;
public function DaeEffectParam(data:XML, effect:DaeEffect) {
super(data, effect.document);
this.effect = effect;
}
public function getFloat(setparams:Object):Number {
var floatXML:XML = data.float[0];
if (floatXML != null) {
return parseNumber(floatXML);
}
var paramRef:XML = data.param.@ref[0];
if (paramRef != null) {
var param:DaeParam = effect.getParam(paramRef.toString(), setparams);
if (param != null) {
return param.getFloat();
}
}
return NaN;
}
public function getColor(setparams:Object):Array {
var colorXML:XML = data.color[0];
if (colorXML != null) {
return parseNumbersArray(colorXML);
}
var paramRef:XML = data.param.@ref[0];
if (paramRef != null) {
var param:DaeParam = effect.getParam(paramRef.toString(), setparams);
if (param != null) {
return param.getFloat4();
}
}
return null;
}
private function get texture():String {
var attr:XML = data.texture.@texture[0];
return (attr == null) ? null : attr.toString();
}
public function getSampler(setparams:Object):DaeParam {
var sid:String = texture;
if (sid != null) {
return effect.getParam(sid, setparams);
}
return null;
}
public function getImage(setparams:Object):DaeImage {
var sampler:DaeParam = getSampler(setparams);
if (sampler != null) {
var surfaceSID:String = sampler.surfaceSID;
if (surfaceSID != null) {
var surface:DaeParam = effect.getParam(surfaceSID, setparams);
if (surface != null) {
return surface.image;
}
} else {
return sampler.image;
}
} else {
// case of 3ds mas default export or so was used, it ignores spec and srores direct link to image
return document.findImageByID(texture);
}
return null;
}
public function get texCoord():String {
var attr:XML = data.texture.@texcoord[0];
return (attr == null) ? null : attr.toString();
}
}
}

View File

@@ -0,0 +1,115 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
import flash.utils.ByteArray;
/**
* @private
*/
public class DaeElement {
use namespace collada;
public var document:DaeDocument;
public var data:XML;
/**
* -1 - not parsed, 0 - parsed with error, 1 - parsed without error.
*/
private var _parsed:int = -1;
private static var _byteArray:ByteArray = new ByteArray();
public function DaeElement(data:XML, document:DaeDocument) {
this.document = document;
this.data = data;
}
public function cloneString(str:String):String {
if(str == null) return null;
_byteArray.position = 0;
_byteArray.writeUTF(str);
_byteArray.position = 0;
var res:String = _byteArray.readUTF();
return res;
}
/**
* Performs pre-setting of object.
* @return <code>false</code> on error.
*/
public function parse():Boolean {
// -1 - not parsed, 0 - parsed with error, 1 - parsed without error.
if (_parsed < 0) {
_parsed = parseImplementation() ? 1 : 0;
return _parsed != 0;
}
return _parsed != 0;
}
/**
* Overridden method <code>parse()</code>.
*/
protected function parseImplementation():Boolean {
return true;
}
/**
* Returns array of String values.
*/
protected function parseStringArray(element:XML):Array {
return element.text().toString().split(/\s+/);
}
protected function parseNumbersArray(element:XML):Array {
var arr:Array = element.text().toString().split(/\s+/);
for (var i:int = 0, count:int = arr.length; i < count; i++) {
var value:String = arr[i];
if (value.indexOf(",") != -1) {
value = value.replace(/,/, ".");
}
arr[i] = parseFloat(value);
}
return arr;
}
protected function parseIntsArray(element:XML):Array {
var arr:Array = element.text().toString().split(/\s+/);
for (var i:int = 0, count:int = arr.length; i < count; i++) {
var value:String = arr[i];
arr[i] = parseInt(value, 10);
}
return arr;
}
protected function parseNumber(element:XML):Number {
var value:String = element.toString().replace(/,/, ".");
return parseFloat(value);
}
public function get id():String {
var idXML:XML = data.@id[0];
return (idXML == null) ? null : idXML.toString();
}
public function get sid():String {
var attr:XML = data.@sid[0];
return (attr == null) ? null : attr.toString();
}
public function get name():String {
var nameXML:XML = data.@name[0];
return (nameXML == null) ? null : nameXML.toString();
}
}
}

View File

@@ -0,0 +1,187 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.VertexAttributes;
import alternativa.engine3d.loaders.ParserMaterial;
import alternativa.engine3d.objects.Mesh;
import alternativa.engine3d.resources.Geometry;
import flash.utils.ByteArray;
import flash.utils.Endian;
/**
* @private
*/
public class DaeGeometry extends DaeElement {
use namespace collada;
use namespace alternativa3d;
internal var geometryVertices:Vector.<DaeVertex>;
internal var primitives:Vector.<DaePrimitive>;
internal var geometry:Geometry;
private var vertices:DaeVertices;
public function DaeGeometry(data:XML, document:DaeDocument) {
super(data, document);
/**
* Items sources, vertices are declared in the <geometry>.
* You should create sources in DaeDocument, not here.
*/
constructVertices();
}
private function constructVertices():void {
var verticesXML:XML = data.mesh.vertices[0];
if (verticesXML != null) {
vertices = new DaeVertices(verticesXML, document);
document.vertices[vertices.id] = vertices;
}
}
override protected function parseImplementation():Boolean {
if (vertices != null) {
parsePrimitives();
vertices.parse();
var numVertices:int = vertices.positions.numbers.length/vertices.positions.stride;
geometry = new Geometry();
geometryVertices = new Vector.<DaeVertex>(numVertices);
var i:int;
var p:DaePrimitive;
var channels:uint = 0;
for (i = 0; i < primitives.length; i++) {
p = primitives[i];
p.parse();
if (p.verticesEquals(vertices)) {
numVertices = geometryVertices.length;
channels |= p.fillGeometry(geometry, geometryVertices);
} else {
// Error, Vertices of another geometry can not be used
}
}
var attributes:Array = new Array(3);
attributes[0] = VertexAttributes.POSITION;
attributes[1] = VertexAttributes.POSITION;
attributes[2] = VertexAttributes.POSITION;
var index:int = 3;
if (channels & DaePrimitive.NORMALS) {
attributes[index++] = VertexAttributes.NORMAL;
attributes[index++] = VertexAttributes.NORMAL;
attributes[index++] = VertexAttributes.NORMAL;
}
if (channels & DaePrimitive.TANGENT4) {
attributes[index++] = VertexAttributes.TANGENT4;
attributes[index++] = VertexAttributes.TANGENT4;
attributes[index++] = VertexAttributes.TANGENT4;
attributes[index++] = VertexAttributes.TANGENT4;
}
for (i = 0; i < 8; i++) {
if (channels & DaePrimitive.TEXCOORDS[i]) {
attributes[index++] = VertexAttributes.TEXCOORDS[i];
attributes[index++] = VertexAttributes.TEXCOORDS[i];
}
}
geometry.addVertexStream(attributes);
numVertices = geometryVertices.length;
var data:ByteArray = new ByteArray();
data.endian = Endian.LITTLE_ENDIAN;
var numMappings:int = attributes.length;
data.length = 4*numMappings*numVertices;
for (i = 0; i < numVertices; i++) {
var vertex:DaeVertex = geometryVertices[i];
if (vertex != null) {
data.position = 4*numMappings*i;
data.writeFloat(vertex.x);
data.writeFloat(vertex.y);
data.writeFloat(vertex.z);
if (vertex.normal != null) {
data.writeFloat(vertex.normal.x);
data.writeFloat(vertex.normal.y);
data.writeFloat(vertex.normal.z);
}
if (vertex.tangent != null) {
data.writeFloat(vertex.tangent.x);
data.writeFloat(vertex.tangent.y);
data.writeFloat(vertex.tangent.z);
data.writeFloat(vertex.tangent.w);
}
for (var j:int = 0; j < vertex.uvs.length; j++) {
data.writeFloat(vertex.uvs[j]);
}
}
}
geometry._vertexStreams[0].data = data;
geometry._numVertices = numVertices;
return true;
}
return false;
}
private function parsePrimitives():void {
primitives = new Vector.<DaePrimitive>();
var children:XMLList = data.mesh.children();
for (var i:int = 0, count:int = children.length(); i < count; i++) {
var child:XML = children[i];
switch (child.localName()) {
case "polygons":
case "polylist":
case "triangles":
case "trifans":
case "tristrips":
var p:DaePrimitive = new DaePrimitive(child, document);
primitives.push(p);
break;
}
}
}
/**
* Creates geometry and returns it as mesh.
* Call <code>parse()</code> before using.
* @param materials Dictionary of materials
*/
public function parseMesh(materials:Object):Mesh {
if (data.mesh.length() > 0) {
var mesh:Mesh = new Mesh();
mesh.geometry = geometry;
for (var i:int = 0; i < primitives.length; i++) {
var p:DaePrimitive = primitives[i];
var instanceMaterial:DaeInstanceMaterial = materials[p.materialSymbol];
var material:ParserMaterial;
if (instanceMaterial != null) {
var daeMaterial:DaeMaterial = instanceMaterial.material;
if (daeMaterial != null) {
daeMaterial.parse();
material = daeMaterial.material;
daeMaterial.used = true;
}
}
mesh.addSurface(material, p.indexBegin, p.numTriangles);
}
mesh.calculateBoundBox();
return mesh;
}
return null;
}
}
}

View File

@@ -0,0 +1,37 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
/**
* @private
*/
public class DaeImage extends DaeElement {
use namespace collada;
public function DaeImage(data:XML, document:DaeDocument) {
super(data, document);
}
public function get init_from():String {
var element:XML = data.init_from[0];
if (element != null) {
if (document.versionMajor > 4) {
var refXML:XML = element.ref[0];
return (refXML == null) ? null : refXML.text().toString();
}
return element.text().toString();
}
return null;
}
}
}

View File

@@ -0,0 +1,63 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
/**
* @private
*/
public class DaeInput extends DaeElement {
use namespace collada;
public function DaeInput(data:XML, document:DaeDocument) {
super(data, document);
}
public function get semantic():String {
var attribute:XML = data.@semantic[0];
return (attribute == null) ? null : attribute.toString();
}
public function get source():XML {
return data.@source[0];
}
public function get offset():int {
var attr:XML = data.@offset[0];
return (attr == null) ? 0 : parseInt(attr.toString(), 10);
}
public function get setNum():int {
var attr:XML = data.@set[0];
return (attr == null) ? 0 : parseInt(attr.toString(), 10);
}
/**
* If DaeSource, located at the link source, is type of Number and
* number of components is not less than specified number, then this method will return it.
*
*/
public function prepareSource(minComponents:int):DaeSource {
var source:DaeSource = document.findSource(this.source);
if (source != null) {
source.parse();
if (source.numbers != null && source.stride >= minComponents) {
return source;
} else {
}
} else {
document.logger.logNotFoundError(data.@source[0]);
}
return null;
}
}
}

View File

@@ -0,0 +1,111 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
import flash.utils.Dictionary;
/**
* @private
*/
public class DaeInstanceController extends DaeElement {
use namespace collada;
public var node:DaeNode;
/**
* List of top-level joints, which have common parent. (List of top-level joints, that have the common parent)
* Call <code>parse()</code> befire using.
*/
public var topmostJoints:Vector.<DaeNode>;
public function DaeInstanceController(data:XML, document:DaeDocument, node:DaeNode) {
super(data, document);
this.node = node;
}
override protected function parseImplementation():Boolean {
var controller:DaeController = this.controller;
if (controller != null) {
topmostJoints = controller.findRootJointNodes(this.skeletons);
if (topmostJoints != null && topmostJoints.length > 1) {
replaceNodesByTopmost(topmostJoints);
}
}
return topmostJoints != null;
}
/**
* Replaces each node in the list with its parent (the parent must be the same for all others node's or be a scene)
* @param nodes not empty array of nodes.
*/
private function replaceNodesByTopmost(nodes:Vector.<DaeNode>):void {
var i:int;
var node:DaeNode, parent:DaeNode;
var numNodes:int = nodes.length;
var parents:Dictionary = new Dictionary();
for (i = 0; i < numNodes; i++) {
node = nodes[i];
for (parent = node.parent; parent != null; parent = parent.parent) {
if (parents[parent]) {
parents[parent]++;
} else {
parents[parent] = 1;
}
}
}
// Replase node with its parent if it has the same parent with each other node or has no parent at all
for (i = 0; i < numNodes; i++) {
node = nodes[i];
while ((parent = node.parent) != null && (parents[parent] != numNodes)) {
node = node.parent;
}
nodes[i] = node;
}
}
private function get controller():DaeController {
var controller:DaeController = document.findController(data.@url[0]);
if (controller == null) {
document.logger.logNotFoundError(data.@url[0]);
}
return controller;
}
private function get skeletons():Vector.<DaeNode> {
var list:XMLList = data.skeleton;
if (list.length() > 0) {
var skeletons:Vector.<DaeNode> = new Vector.<DaeNode>();
for (var i:int = 0, count:int = list.length(); i < count; i++) {
var skeletonXML:XML = list[i];
var skel:DaeNode = document.findNode(skeletonXML.text()[0]);
if (skel != null) {
skeletons.push(skel);
} else {
document.logger.logNotFoundError(skeletonXML);
}
}
return skeletons;
}
return null;
}
public function parseSkin(materials:Object):DaeObject {
var controller:DaeController = this.controller;
if (controller != null) {
controller.parse();
return controller.parseSkin(materials, topmostJoints, this.skeletons);
}
return null;
}
}
}

View File

@@ -0,0 +1,49 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
/**
* @private
*/
public class DaeInstanceMaterial extends DaeElement {
use namespace collada;
public function DaeInstanceMaterial(data:XML, document:DaeDocument) {
super(data, document);
}
public function get symbol():String {
var attribute:XML = data.@symbol[0];
return (attribute == null) ? null : attribute.toString();
}
private function get target():XML {
return data.@target[0];
}
public function get material():DaeMaterial {
var mat:DaeMaterial = document.findMaterial(target);
if (mat == null) {
document.logger.logNotFoundError(target);
}
return mat;
}
public function getBindVertexInputSetNum(semantic:String):int {
var bindVertexInputXML:XML = data.bind_vertex_input.(@semantic == semantic)[0];
if (bindVertexInputXML == null) return 0;
var setNumXML:XML = bindVertexInputXML.@input_set[0];
return (setNumXML == null) ? 0 : parseInt(setNumXML.toString(), 10);
}
}
}

View File

@@ -0,0 +1,119 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.core.Light3D;
import alternativa.engine3d.lights.AmbientLight;
import alternativa.engine3d.lights.DirectionalLight;
import alternativa.engine3d.lights.OmniLight;
import alternativa.engine3d.lights.SpotLight;
/**
* @private
*/
public class DaeLight extends DaeElement {
use namespace collada;
public function DaeLight(data:XML, document:DaeDocument) {
super(data, document);
}
private function float4ToUint(value:Array):uint {
var r:uint = (value[0] * 255);
var g:uint = (value[1] * 255);
var b:uint = (value[2] * 255);
return (r << 16) | (g << 8) | b | 0xFF000000;
}
public function get revertDirection():Boolean {
var info:XML = data.technique_common.children()[0];
return (info == null) ? false : (info.localName() == "directional" || info.localName() == "spot");
}
public function parseLight():Light3D {
var info:XML = data.technique_common.children()[0];
var extra:XML = data.extra.technique.(@profile[0] == "OpenCOLLADA3dsMax").light[0];
var light:Light3D = null;
if (info != null) {
var color:uint = float4ToUint(parseNumbersArray(info.color[0]));
var constantAttenuationXML:XML;
var linearAttenuationXML:XML;
var linearAttenuation:Number = 0;
var attenuationStart:Number = 0;
var attenuationEnd:Number = 1;
switch (info.localName()) {
case "ambient":
light = new AmbientLight(color);
break;
case "directional":
var dLight:DirectionalLight = new DirectionalLight(color);
light = dLight;
break;
case "point":
if (extra != null) {
attenuationStart = parseNumber(extra.attenuation_far_start[0]);
attenuationEnd = parseNumber(extra.attenuation_far_end[0]);
} else {
constantAttenuationXML = info.constant_attenuation[0];
linearAttenuationXML = info.linear_attenuation[0];
if (constantAttenuationXML != null) {
attenuationStart = -parseNumber(constantAttenuationXML);
}
if (linearAttenuationXML != null) {
linearAttenuation = parseNumber(linearAttenuationXML);
}
if (linearAttenuation > 0) {
attenuationEnd = 1/linearAttenuation + attenuationStart;
} else {
attenuationEnd = attenuationStart + 1;
}
}
var oLight:OmniLight = new OmniLight(color, attenuationStart, attenuationEnd);
light = oLight;
break;
case "spot":
var hotspot:Number = 0;
var fallof:Number = Math.PI/4;
const DEG2RAD:Number = Math.PI/180;
if (extra != null) {
attenuationStart = parseNumber(extra.attenuation_far_start[0]);
attenuationEnd = parseNumber(extra.attenuation_far_end[0]);
hotspot = DEG2RAD * parseNumber(extra.hotspot_beam[0]);
fallof = DEG2RAD * parseNumber(extra.falloff[0]);
} else {
constantAttenuationXML = info.constant_attenuation[0];
linearAttenuationXML = info.linear_attenuation[0];
if (constantAttenuationXML != null) {
attenuationStart = -parseNumber(constantAttenuationXML);
}
if (linearAttenuationXML != null) {
linearAttenuation = parseNumber(linearAttenuationXML);
}
if (linearAttenuation > 0) {
attenuationEnd = 1/linearAttenuation + attenuationStart;
} else {
attenuationEnd = attenuationStart + 1;
}
}
var sLight:SpotLight = new SpotLight(color, attenuationStart, attenuationEnd, hotspot, fallof);
light = sLight;
break;
}
}
if (extra != null) {
light.intensity = parseNumber(extra.multiplier[0]);
}
return light;
}
}
}

View File

@@ -0,0 +1,62 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
/**
* @private
*/
public class DaeLogger {
public function DaeLogger() {
}
private function logMessage(message:String, element:XML):void {
var index:int = 0;
var name:String = (element.nodeKind() == "attribute") ? "@" + element.localName() : element.localName() + ((index > 0) ? "[" + index + "]" : "");
var parent:* = element.parent();
while (parent != null) {
// index = parent.childIndex();
name = parent.localName() + ((index > 0) ? "[" + index + "]" : "") + "." + name;
parent = parent.parent();
}
trace(message, '| "' + name + '"');
}
private function logError(message:String, element:XML):void {
logMessage("[ERROR] " + message, element);
}
public function logExternalError(element:XML):void {
logError("External urls don't supported", element);
}
public function logSkewError(element:XML):void {
logError("<skew> don't supported", element);
}
public function logJointInAnotherSceneError(element:XML):void {
logError("Joints in different scenes don't supported", element);
}
public function logInstanceNodeError(element:XML):void {
logError("<instance_node> don't supported", element);
}
public function logNotFoundError(element:XML):void {
logError("Element with url \"" + element.toString() + "\" not found", element);
}
public function logNotEnoughDataError(element:XML):void {
logError("Not enough data", element);
}
}
}

View File

@@ -0,0 +1,72 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.loaders.ParserMaterial;
/**
* @private
*/
public class DaeMaterial extends DaeElement {
use namespace collada;
/**
* Material of engine.
* Call <code>parse()</code> before using.
*/
public var material:ParserMaterial;
/**
* Name of texture channel for color map of object.
* Call <code>parse()</code> before using.
*/
public var mainTexCoords:String;
/**
* If <code>true</code>material is in use.
*/
public var used:Boolean = false;
public function DaeMaterial(data:XML, document:DaeDocument) {
super(data, document);
}
private function parseSetParams():Object {
var params:Object = new Object();
var list:XMLList = data.instance_effect.setparam;
for each (var element:XML in list) {
var param:DaeParam = new DaeParam(element, document);
params[param.ref] = param;
}
return params;
}
private function get effectURL():XML {
return data.instance_effect.@url[0];
}
override protected function parseImplementation():Boolean {
var effect:DaeEffect = document.findEffect(effectURL);
if (effect != null) {
effect.parse();
material = effect.getMaterial(parseSetParams());
mainTexCoords = effect.mainTexCoords;
if (material != null) {
material.name = cloneString(name);
}
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,517 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.animation.AnimationClip;
import alternativa.engine3d.animation.keys.NumberTrack;
import alternativa.engine3d.animation.keys.Track;
import alternativa.engine3d.animation.keys.TransformTrack;
import alternativa.engine3d.core.Light3D;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.objects.Mesh;
import alternativa.engine3d.objects.Skin;
import flash.geom.Matrix3D;
import flash.geom.Vector3D;
/**
* @private
*/
public class DaeNode extends DaeElement {
use namespace collada;
public var scene:DaeVisualScene;
public var parent:DaeNode;
// Skin or top-level joint.
public var skinOrTopmostJoint:Boolean = false;
/**
* Animation channels of this node.
*/
private var channels:Vector.<DaeChannel>;
/**
* Vector of controllers, which have reference to this node.
*/
private var instanceControllers:Vector.<DaeInstanceController>;
/**
* Array of nodes at this node.
*/
public var nodes:Vector.<DaeNode>;
/**
* Array of objects at this node.
* Call <code>parse()</code> before using.
*
*/
public var objects:Vector.<DaeObject>;
/**
* Vector of skins at this node.
* Call <code>parse()</code> before using.
*
*/
public var skins:Vector.<DaeObject>;
/**
* Name of object for animation
*/
public function get animName():String {
var n:String = this.name;
return (n == null) ? this.id : n;
}
/**
* Create node from xml. Child nodes are created recursively.
*/
public function DaeNode(data:XML, document:DaeDocument, scene:DaeVisualScene = null, parent:DaeNode = null) {
super(data, document);
this.scene = scene;
this.parent = parent;
// Others node's declares inside <node>
constructNodes();
}
private function constructNodes():void {
var nodesList:XMLList = data.node;
var count:int = nodesList.length();
nodes = new Vector.<DaeNode>(count);
for (var i:int = 0; i < count; i++) {
var node:DaeNode = new DaeNode(nodesList[i], document, scene, this);
if (node.id != null) {
document.nodes[node.id] = node;
}
nodes[i] = node;
}
}
internal function registerInstanceControllers():void {
var instanceControllerXMLs:XMLList = data.instance_controller;
var i:int;
var count:int = instanceControllerXMLs.length()
for (i = 0; i < count; i++) {
skinOrTopmostJoint = true;
var instanceControllerXML:XML = instanceControllerXMLs[i];
var instanceController:DaeInstanceController = new DaeInstanceController(instanceControllerXML, document, this);
if (instanceController.parse()) {
var jointNodes:Vector.<DaeNode> = instanceController.topmostJoints;
var numNodes:int = jointNodes.length;
if (numNodes > 0) {
var jointNode:DaeNode = jointNodes[0];
jointNode.addInstanceController(instanceController);
for (var j:int = 0; j < numNodes; j++) {
jointNodes[j].skinOrTopmostJoint = true;
}
}
}
}
count = nodes.length;
for (i = 0; i < count; i++) {
nodes[i].registerInstanceControllers();
}
}
public function addChannel(channel:DaeChannel):void {
if (channels == null) {
channels = new Vector.<DaeChannel>();
}
channels.push(channel);
}
public function addInstanceController(controller:DaeInstanceController):void {
if (instanceControllers == null) {
instanceControllers = new Vector.<DaeInstanceController>();
}
instanceControllers.push(controller);
}
override protected function parseImplementation():Boolean {
this.skins = parseSkins();
this.objects = parseObjects();
return true;
}
private function parseInstanceMaterials(geometry:XML):Object {
var instances:Object = new Object();
var list:XMLList = geometry.bind_material.technique_common.instance_material;
for (var i:int = 0, count:int = list.length(); i < count; i++) {
var instance:DaeInstanceMaterial = new DaeInstanceMaterial(list[i], document);
instances[instance.symbol] = instance;
}
return instances;
}
/**
* Returns node by Sid.
*/
public function getNodeBySid(sid:String):DaeNode {
if (sid == this.sid) {
return this;
}
var levelNodes:Vector.<Vector.<DaeNode> > = new Vector.<Vector.<DaeNode> >;
var levelNodes2:Vector.<Vector.<DaeNode> > = new Vector.<Vector.<DaeNode> >;
levelNodes.push(nodes);
var len:int = levelNodes.length;
while (len > 0) {
for (var i:int = 0; i < len; i++) {
var children:Vector.<DaeNode> = levelNodes[i];
var count:int = children.length;
for (var j:int = 0; j < count; j++) {
var node:DaeNode = children[j];
if (node.sid == sid) {
return node;
}
if (node.nodes.length > 0) {
levelNodes2.push(node.nodes);
}
}
}
var temp:Vector.<Vector.<DaeNode> > = levelNodes;
levelNodes = levelNodes2;
levelNodes2 = temp;
levelNodes2.length = 0;
len = levelNodes.length;
}
return null;
}
/**
* Parses and returns array of skins, associated with this node.
*/
public function parseSkins():Vector.<DaeObject> {
if (instanceControllers == null) {
return null;
}
var skins:Vector.<DaeObject> = new Vector.<DaeObject>();
for (var i:int = 0, count:int = instanceControllers.length; i < count; i++) {
var instanceController:DaeInstanceController = instanceControllers[i];
instanceController.parse();
var skinAndAnimatedJoints:DaeObject = instanceController.parseSkin(parseInstanceMaterials(instanceController.data));
if (skinAndAnimatedJoints != null) {
var skin:Skin = Skin(skinAndAnimatedJoints.object);
// Name is got from node, that contains instance_controller.
skin.name = cloneString(instanceController.node.name);
// Not apply transformation and animation for skin. It specifies at root joints.
skins.push(skinAndAnimatedJoints);
}
}
return (skins.length > 0) ? skins : null;
}
/**
* Parses and returns array of objects, associated with this node.
* Can be Mesh or Object3D, if type of object is unknown.
*/
public function parseObjects():Vector.<DaeObject> {
var objects:Vector.<DaeObject> = new Vector.<DaeObject>();
var children:XMLList = data.children();
var i:int, count:int;
for (i = 0, count = children.length(); i < count; i++) {
var child:XML = children[i];
switch (child.localName()) {
case "instance_light":
var lightInstance:DaeLight = document.findLight(child.@url[0]);
if (lightInstance != null) {
var light:Light3D = lightInstance.parseLight();
if (light != null) {
light.name = cloneString(name);
if (lightInstance.revertDirection) {
// Rotate 180 degrees along the x-axis, for correspondence to engine
var rotXMatrix:Matrix3D = new Matrix3D();
rotXMatrix.appendRotation(180, Vector3D.X_AXIS);
// Not upload animations yet for these light sources
objects.push(new DaeObject(applyTransformations(light, rotXMatrix)));
} else {
objects.push(applyAnimation(applyTransformations(light)));
}
}
} else {
document.logger.logNotFoundError(child.@url[0]);
}
break;
case "instance_geometry":
var geom:DaeGeometry = document.findGeometry(child.@url[0]);
if (geom != null) {
geom.parse();
var mesh:Mesh = geom.parseMesh(parseInstanceMaterials(child));
if(mesh != null){
mesh.name = cloneString(name);
objects.push(applyAnimation(applyTransformations(mesh)));
}
} else {
document.logger.logNotFoundError(child.@url[0]);
}
break;
case "instance_node":
document.logger.logInstanceNodeError(child);
break;
}
}
return (objects.length > 0) ? objects : null;
}
/**
* Returns transformation of node as a matrix.
* @param initialMatrix To this matrix tranformation will appended.
*/
private function getMatrix(initialMatrix:Matrix3D = null):Matrix3D {
var matrix:Matrix3D = (initialMatrix == null) ? new Matrix3D() : initialMatrix;
var components:Array;
var children:XMLList = data.children();
for (var i:int = children.length() - 1; i >= 0; i--) {
//Transformations are append from the end to begin
var child:XML = children[i];
var sid:XML = child.@sid[0];
if (sid != null && sid.toString() == "post-rotationY") {
// Default 3dsmax exporter writes some trash which ignores
continue;
}
switch (child.localName()) {
case "scale" : {
components = parseNumbersArray(child);
matrix.appendScale(components[0], components[1], components[2]);
break;
}
case "rotate" : {
components = parseNumbersArray(child);
matrix.appendRotation(components[3], new Vector3D(components[0], components[1], components[2]));
break;
}
case "translate" : {
components = parseNumbersArray(child);
matrix.appendTranslation(components[0]*document.unitScaleFactor,
components[1]*document.unitScaleFactor, components[2]*document.unitScaleFactor);
break;
}
case "matrix" : {
components = parseNumbersArray(child);
matrix.append(new Matrix3D(Vector.<Number>([components[0], components[4], components[8], components[12],
components[1], components[5], components[9], components[13],
components[2], components[6], components[10], components[14],
components[3]*document.unitScaleFactor ,components[7]*document.unitScaleFactor, components[11]*document.unitScaleFactor, components[15]])));
break;
}
case "lookat" : {
break;
}
case "skew" : {
document.logger.logSkewError(child);
break;
}
}
}
return matrix;
}
/**
* Apply transformation to object.
* @param prepend If is not <code>null</code> transformation will added to this matrix.
*/
public function applyTransformations(object:Object3D, prepend:Matrix3D = null, append:Matrix3D = null):Object3D {
var matrix:Matrix3D = getMatrix(prepend);
if (append != null) {
matrix.append(append);
}
var vs:Vector.<Vector3D> = matrix.decompose();
var t:Vector3D = vs[0];
var r:Vector3D = vs[1];
var s:Vector3D = vs[2];
object.x = t.x;
object.y = t.y;
object.z = t.z;
object.rotationX = r.x;
object.rotationY = r.y;
object.rotationZ = r.z;
object.scaleX = s.x;
object.scaleY = s.y;
object.scaleZ = s.z;
return object;
}
public function applyAnimation(object:Object3D):DaeObject {
var animation:AnimationClip = parseAnimation(object);
if (animation == null) {
return new DaeObject(object);
}
object.name = animName;
animation.attach(object, false);
return new DaeObject(object, animation);
}
/**
* Returns animation of node.
*/
public function parseAnimation(object:Object3D = null):AnimationClip {
if (channels == null || !hasTransformationAnimation()) {
return null;
}
var channel:DaeChannel = getChannel(DaeChannel.PARAM_MATRIX);
if (channel != null) {
return createClip(channel.tracks);
}
var clip:AnimationClip = new AnimationClip();
var components:Vector.<Vector3D> = (object != null) ? null : getMatrix().decompose();
// Translation
channel = getChannel(DaeChannel.PARAM_TRANSLATE);
if (channel != null) {
addTracksToClip(clip, channel.tracks);
} else {
channel = getChannel(DaeChannel.PARAM_TRANSLATE_X);
if (channel != null) {
addTracksToClip(clip, channel.tracks);
} else {
clip.addTrack(createValueStaticTrack("x", (object == null) ? components[0].x : object.x));
}
channel = getChannel(DaeChannel.PARAM_TRANSLATE_Y);
if (channel != null) {
addTracksToClip(clip, channel.tracks);
} else {
clip.addTrack(createValueStaticTrack("y", (object == null) ? components[0].y : object.y));
}
channel = getChannel(DaeChannel.PARAM_TRANSLATE_Z);
if (channel != null) {
addTracksToClip(clip, channel.tracks);
} else {
clip.addTrack(createValueStaticTrack("z", (object == null) ? components[0].z : object.z));
}
}
// Rotation
channel = getChannel(DaeChannel.PARAM_ROTATION_X);
if (channel != null) {
addTracksToClip(clip, channel.tracks);
} else {
clip.addTrack(createValueStaticTrack("rotationX", (object == null) ? components[1].x : object.rotationX));
}
channel = getChannel(DaeChannel.PARAM_ROTATION_Y);
if (channel != null) {
addTracksToClip(clip, channel.tracks);
} else {
clip.addTrack(createValueStaticTrack("rotationY", (object == null) ? components[1].y : object.rotationY));
}
channel = getChannel(DaeChannel.PARAM_ROTATION_Z);
if (channel != null) {
addTracksToClip(clip, channel.tracks);
} else {
clip.addTrack(createValueStaticTrack("rotationZ", (object == null) ? components[1].z : object.rotationZ));
}
// Scale
channel = getChannel(DaeChannel.PARAM_SCALE);
if (channel != null) {
addTracksToClip(clip, channel.tracks);
} else {
channel = getChannel(DaeChannel.PARAM_SCALE_X);
if (channel != null) {
addTracksToClip(clip, channel.tracks);
} else {
clip.addTrack(createValueStaticTrack("scaleX", (object == null) ? components[2].x : object.scaleX));
}
channel = getChannel(DaeChannel.PARAM_SCALE_Y);
if (channel != null) {
addTracksToClip(clip, channel.tracks);
} else {
clip.addTrack(createValueStaticTrack("scaleY", (object == null) ? components[2].y : object.scaleY));
}
channel = getChannel(DaeChannel.PARAM_SCALE_Z);
if (channel != null) {
addTracksToClip(clip, channel.tracks);
} else {
clip.addTrack(createValueStaticTrack("scaleZ", (object == null) ? components[2].z : object.scaleZ));
}
}
if (clip.numTracks > 0) {
return clip;
}
return null;
}
private function createClip(tracks:Vector.<Track>):AnimationClip {
var clip:AnimationClip = new AnimationClip();
for (var i:int = 0, count:int = tracks.length; i < count; i++) {
clip.addTrack(tracks[i]);
}
return clip;
}
private function addTracksToClip(clip:AnimationClip, tracks:Vector.<Track>):void {
for (var i:int = 0, count:int = tracks.length; i < count; i++) {
clip.addTrack(tracks[i]);
}
}
private function hasTransformationAnimation():Boolean {
for (var i:int = 0, count:int = channels.length; i < count; i++) {
var channel:DaeChannel = channels[i];
channel.parse();
var result:Boolean = channel.animatedParam == DaeChannel.PARAM_MATRIX;
result ||= channel.animatedParam == DaeChannel.PARAM_TRANSLATE;
result ||= channel.animatedParam == DaeChannel.PARAM_TRANSLATE_X;
result ||= channel.animatedParam == DaeChannel.PARAM_TRANSLATE_Y;
result ||= channel.animatedParam == DaeChannel.PARAM_TRANSLATE_Z;
result ||= channel.animatedParam == DaeChannel.PARAM_ROTATION_X;
result ||= channel.animatedParam == DaeChannel.PARAM_ROTATION_Y;
result ||= channel.animatedParam == DaeChannel.PARAM_ROTATION_Z;
result ||= channel.animatedParam == DaeChannel.PARAM_SCALE;
result ||= channel.animatedParam == DaeChannel.PARAM_SCALE_X;
result ||= channel.animatedParam == DaeChannel.PARAM_SCALE_Y;
result ||= channel.animatedParam == DaeChannel.PARAM_SCALE_Z;
if (result) {
return true;
}
}
return false;
}
private function getChannel(param:String):DaeChannel {
for (var i:int = 0, count:int = channels.length; i < count; i++) {
var channel:DaeChannel = channels[i];
channel.parse();
if (channel.animatedParam == param) {
return channel;
}
}
return null;
}
private function concatTracks(source:Vector.<Track>, dest:Vector.<Track>):void {
for (var i:int = 0, count:int = source.length; i < count; i++) {
dest.push(source[i]);
}
}
private function createValueStaticTrack(property:String, value:Number):Track {
var track:NumberTrack = new NumberTrack(animName, property);
track.addKey(0, value);
return track;
}
public function createStaticTransformTrack():TransformTrack {
var track:TransformTrack = new TransformTrack(animName);
track.addKey(0, getMatrix());
return track;
}
public function get layer():String {
var layerXML:XML = data.@layer[0];
return (layerXML == null) ? null : layerXML.toString();
}
}
}

View File

@@ -0,0 +1,31 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.animation.AnimationClip;
import alternativa.engine3d.core.Object3D;
/**
* @private
*/
public class DaeObject {
public var object:Object3D;
public var animation:AnimationClip;
public var jointNode:DaeNode;
public function DaeObject(object:Object3D, animation:AnimationClip = null) {
this.object = object;
this.animation = animation;
}
}
}

View File

@@ -0,0 +1,89 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
/**
* @private
*/
public class DaeParam extends DaeElement {
use namespace collada;
public function DaeParam(data:XML, document:DaeDocument) {
super(data, document);
}
public function get ref():String {
var attribute:XML = data.@ref[0];
return (attribute == null) ? null : attribute.toString();
}
public function getFloat():Number {
var floatXML:XML = data.float[0];
if (floatXML != null) {
return parseNumber(floatXML);
}
return NaN;
}
public function getFloat4():Array {
var element:XML = data.float4[0];
var components:Array;
if (element == null) {
element = data.float3[0];
if (element != null) {
components = parseNumbersArray(element);
components[3] = 1.0;
}
} else {
components = parseNumbersArray(element);
}
return components;
}
/**
* Returns Sid of a parameter type of surface. Only for sampler2D and Collada ver. 1.4
*/
public function get surfaceSID():String {
var element:XML = data.sampler2D.source[0];
return (element == null) ? null : element.text().toString();
}
public function get wrap_s():String {
var element:XML = data.sampler2D.wrap_s[0];
return (element == null) ? null : element.text().toString();
}
public function get image():DaeImage {
var surface:XML = data.surface[0];
var image:DaeImage;
if (surface != null) {
// Collada 1.4
var init_from:XML = surface.init_from[0];
if (init_from == null) {
// Error
return null;
}
image = document.findImageByID(init_from.text().toString());
} else {
// Collada 1.5
var imageIDXML:XML = data.instance_image.@url[0];
if (imageIDXML == null) {
// error
return null;
}
image = document.findImage(imageIDXML);
}
return image;
}
}
}

View File

@@ -0,0 +1,331 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.resources.Geometry;
use namespace collada;
use namespace alternativa3d;
/**
* @private
*/
public class DaePrimitive extends DaeElement {
internal static const NORMALS:int = 1;
internal static const TANGENT4:int = 2;
internal static const TEXCOORDS:Vector.<uint> = Vector.<uint>([8, 16, 32, 64, 128, 256, 512, 1024]);
internal var verticesInput:DaeInput;
internal var texCoordsInputs:Vector.<DaeInput>;
internal var normalsInput:DaeInput;
internal var biNormalsInputs:Vector.<DaeInput>;
internal var tangentsInputs:Vector.<DaeInput>;
internal var indices:Vector.<uint>;
internal var inputsStride:int;
public var indexBegin:int;
public var numTriangles:int;
public function DaePrimitive(data:XML, document:DaeDocument) {
super(data, document);
}
override protected function parseImplementation():Boolean {
parseInputs();
parseIndices();
return true;
}
private function get type():String {
return data.localName() as String;
}
private function parseInputs():void {
texCoordsInputs = new Vector.<DaeInput>();
tangentsInputs = new Vector.<DaeInput>();
biNormalsInputs = new Vector.<DaeInput>();
var inputsList:XMLList = data.input;
var maxInputOffset:int = 0;
for (var i:int = 0, count:int = inputsList.length(); i < count; i++) {
var input:DaeInput = new DaeInput(inputsList[i], document);
var semantic:String = input.semantic;
if (semantic != null) {
switch (semantic) {
case "VERTEX" :
if (verticesInput == null) {
verticesInput = input;
}
break;
case "TEXCOORD" :
texCoordsInputs.push(input);
break;
case "NORMAL":
if (normalsInput == null) {
normalsInput = input;
}
break;
case "TEXTANGENT":
tangentsInputs.push(input);
break;
case "TEXBINORMAL":
biNormalsInputs.push(input);
break;
}
}
var offset:int = input.offset;
maxInputOffset = (offset > maxInputOffset) ? offset : maxInputOffset;
}
inputsStride = maxInputOffset + 1;
}
private function parseIndices():void {
indices = new Vector.<uint>();
var array:Array;
var vcount:Vector.<int> = new Vector.<int>();
var i:int = 0;
var count:int = 0;
switch (data.localName()) {
case "polylist":
case "polygons":
var vcountXML:XMLList = data.vcount;
array = parseStringArray(vcountXML[0]);
for (i = 0,count = array.length; i < count; i++) {
vcount.push(parseInt(array[i]));
}
case "triangles":
var pList:XMLList = data.p;
for (i = 0,count = pList.length(); i < count; i++) {
array = parseStringArray(pList[i]);
for (var j:int = 0; j < array.length; j++) {
indices.push(parseInt(array[j], 10));
}
if (vcount.length > 0) {
indices = triangulate(indices, vcount);
}
}
break;
}
}
private function triangulate(input:Vector.<uint>, vcount:Vector.<int>):Vector.<uint> {
var res:Vector.<uint> = new Vector.<uint>();
var indexIn:uint, indexOut:uint = 0;
var i:int, j:int, k:int, count:int;
for (i = 0,count = vcount.length; i < count; i++) {
var verticesCount:int = vcount[i];
var attributesCount:int = verticesCount*inputsStride;
if (verticesCount == 3) {
for (j = 0; j < attributesCount; j++,indexIn++,indexOut++) {
res[indexOut] = input[indexIn];
}
} else {
for (j = 1; j < verticesCount - 1; j++) {
// 0 - vertex
for (k = 0; k < inputsStride; k++,indexOut++) {
res[indexOut] = input[int(indexIn + k)];
}
// 1 - vertex
for (k = 0; k < inputsStride; k++,indexOut++) {
res[indexOut] = input[int(indexIn + inputsStride*j + k)];
}
// 2 - vertex
for (k = 0; k < inputsStride; k++,indexOut++) {
res[indexOut] = input[int(indexIn + inputsStride*(j + 1) + k)];
}
}
indexIn += inputsStride*verticesCount;
}
}
return res;
}
public function fillGeometry(geometry:Geometry, vertices:Vector.<DaeVertex>):uint {
if (verticesInput == null) {
// Error
return 0;
}
verticesInput.parse();
var numIndices:int = indices.length;
var daeVertices:DaeVertices = document.findVertices(verticesInput.source);
if (daeVertices == null) {
document.logger.logNotFoundError(verticesInput.source);
return 0;
}
daeVertices.parse();
var positionSource:DaeSource = daeVertices.positions;
var vertexStride:int = 3; // XYZ
var mainSource:DaeSource = positionSource;
var mainInput:DaeInput = verticesInput;
var tangentSource:DaeSource;
var binormalSource:DaeSource;
var channels:uint = 0;
var normalSource:DaeSource;
var inputOffsets:Vector.<int> = new Vector.<int>();
inputOffsets.push(verticesInput.offset);
if (normalsInput != null) {
normalSource = normalsInput.prepareSource(3);
inputOffsets.push(normalsInput.offset);
vertexStride += 3;
channels |= NORMALS;
if (tangentsInputs.length > 0 && biNormalsInputs.length > 0) {
tangentSource = tangentsInputs[0].prepareSource(3);
inputOffsets.push(tangentsInputs[0].offset);
binormalSource = biNormalsInputs[0].prepareSource(3);
inputOffsets.push(biNormalsInputs[0].offset);
vertexStride += 4;
channels |= TANGENT4;
}
}
var textureSources:Vector.<DaeSource> = new Vector.<DaeSource>();
var numTexCoordsInputs:int = texCoordsInputs.length;
if (numTexCoordsInputs > 8) {
// TODO: Warning
numTexCoordsInputs = 8;
}
for (var i:int = 0; i < numTexCoordsInputs; i++) {
var s:DaeSource = texCoordsInputs[i].prepareSource(2);
textureSources.push(s);
inputOffsets.push(texCoordsInputs[i].offset);
vertexStride += 2;
channels |= TEXCOORDS[i];
}
var verticesLength:int = vertices.length;
// Make geometry data
var index:uint;
var vertex:DaeVertex;
indexBegin = geometry._indices.length;
for (i = 0; i < numIndices; i += inputsStride) {
index = indices[int(i + mainInput.offset)];
vertex = vertices[index];
if (vertex == null || !isEqual(vertex, indices, i, inputOffsets)) {
if (vertex != null) {
// Add to end
index = verticesLength++;
}
vertex = new DaeVertex();
vertices[index] = vertex;
vertex.vertexInIndex = indices[int(i + verticesInput.offset)];
vertex.addPosition(positionSource.numbers, vertex.vertexInIndex, positionSource.stride, document.unitScaleFactor);
if (normalSource != null) {
vertex.addNormal(normalSource.numbers, indices[int(i + normalsInput.offset)], normalSource.stride);
}
if (tangentSource != null) {
vertex.addTangentBiDirection(tangentSource.numbers, indices[int(i + tangentsInputs[0].offset)], tangentSource.stride, binormalSource.numbers, indices[int(i + biNormalsInputs[0].offset)], binormalSource.stride);
}
for (var j:int = 0; j < textureSources.length; j++) {
vertex.appendUV(textureSources[j].numbers, indices[int(i + texCoordsInputs[j].offset)], textureSources[j].stride);
}
}
vertex.vertexOutIndex = index;
geometry._indices.push(index);
}
numTriangles = (geometry._indices.length - indexBegin)/3;
return channels;
}
private function isEqual(vertex:DaeVertex, indices:Vector.<uint>, index:int, offsets:Vector.<int>):Boolean {
var numOffsets:int = offsets.length;
for (var j:int = 0; j < numOffsets; j++) {
if (vertex.indices[j] != indices[int(index + offsets[j])]) {
return false;
}
}
return true;
}
private function findInputBySet(inputs:Vector.<DaeInput>, setNum:int):DaeInput {
for (var i:int = 0, numInputs:int = inputs.length; i < numInputs; i++) {
var input:DaeInput = inputs[i];
if (input.setNum == setNum) {
return input;
}
}
return null;
}
/**
* Returns array of texture channels data. First element stores channel with mainSetNum.
*/
private function getTexCoordsDatas(mainSetNum:int):Vector.<VertexChannelData> {
var mainInput:DaeInput = findInputBySet(texCoordsInputs, mainSetNum);
var i:int;
var numInputs:int = texCoordsInputs.length;
var datas:Vector.<VertexChannelData> = new Vector.<VertexChannelData>();
for (i = 0; i < numInputs; i++) {
var texCoordsInput:DaeInput = texCoordsInputs[i];
var texCoordsSource:DaeSource = texCoordsInput.prepareSource(2);
if (texCoordsSource != null) {
var data:VertexChannelData = new VertexChannelData(texCoordsSource.numbers, texCoordsSource.stride, texCoordsInput.offset, texCoordsInput.setNum);
if (texCoordsInput == mainInput) {
datas.unshift(data);
} else {
datas.push(data);
}
}
}
return (datas.length > 0) ? datas : null;
}
/**
* Compare vertices of the privitive with given at <code>otherVertices</code> parameter vertices.
* Call <code>parse()</code> before using.
*/
public function verticesEquals(otherVertices:DaeVertices):Boolean {
var vertices:DaeVertices = document.findVertices(verticesInput.source);
if (vertices == null) {
document.logger.logNotFoundError(verticesInput.source);
}
return vertices == otherVertices;
}
public function get materialSymbol():String {
var attr:XML = data.@material[0];
return (attr == null) ? null : attr.toString();
}
}
}
import flash.geom.Point;
class VertexChannelData {
public var values:Vector.<Number>;
public var stride:int;
public var offset:int;
public var index:int;
public var channel:Vector.<Point>;
public var inputSet:int;
public function VertexChannelData(values:Vector.<Number>, stride:int, offset:int, inputSet:int = 0) {
this.values = values;
this.stride = stride;
this.offset = offset;
this.inputSet = inputSet;
}
}

View File

@@ -0,0 +1,118 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.animation.keys.NumberTrack;
import alternativa.engine3d.animation.keys.Track;
import alternativa.engine3d.animation.keys.TransformTrack;
import flash.geom.Matrix3D;
use namespace collada;
/**
* @private
*/
public class DaeSampler extends DaeElement {
private var times:Vector.<Number>;
private var values:Vector.<Number>;
private var timesStride:int;
private var valuesStride:int;
public function DaeSampler(data:XML, document:DaeDocument) {
super(data, document);
}
override protected function parseImplementation():Boolean {
var inputsList:XMLList = data.input;
var inputSource:DaeSource;
var outputSource:DaeSource;
for (var i:int = 0, count:int = inputsList.length(); i < count; i++) {
var input:DaeInput = new DaeInput(inputsList[i], document);
var semantic:String = input.semantic;
if (semantic != null) {
switch (semantic) {
case "INPUT" :
inputSource = input.prepareSource(1);
if (inputSource != null) {
times = inputSource.numbers;
timesStride = inputSource.stride;
}
break;
case "OUTPUT" :
outputSource = input.prepareSource(1);
if (outputSource != null) {
values = outputSource.numbers;
valuesStride = outputSource.stride;
}
break;
}
}
}
return true;
}
public function parseNumbersTrack(objectName:String, property:String):NumberTrack {
if (times != null && values != null && timesStride > 0) {
var track:NumberTrack = new NumberTrack(objectName, property);
var count:int = times.length/timesStride;
for (var i:int = 0; i < count; i++) {
track.addKey(times[int(timesStride*i)], values[int(valuesStride*i)]);
}
// TODO:: Exceptions with indices
return track;
}
return null;
}
public function parseTransformationTrack(objectName:String):Track {
if (times != null && values != null && timesStride != 0) {
var track:TransformTrack = new TransformTrack(objectName);
var count:int = times.length/timesStride;
for (var i:int = 0; i < count; i++) {
var index:int = valuesStride*i;
var matrix:Matrix3D = new Matrix3D(Vector.<Number>([values[index], values[index + 4], values[index + 8], values[index + 12],
values[index + 1], values[index + 5], values[index + 9], values[index + 13],
values[index + 2], values[index + 6], values[index + 10], values[index + 14],
values[index + 3] ,values[index + 7], values[index + 11], values[index + 15]]));
track.addKey(times[i*timesStride], matrix);
}
return track;
}
return null;
}
public function parsePointsTracks(objectName:String, xProperty:String, yProperty:String, zProperty:String):Vector.<Track> {
if (times != null && values != null && timesStride != 0) {
var xTrack:NumberTrack = new NumberTrack(objectName, xProperty);
xTrack.object = objectName;
var yTrack:NumberTrack = new NumberTrack(objectName, yProperty);
yTrack.object = objectName;
var zTrack:NumberTrack = new NumberTrack(objectName, zProperty);
zTrack.object = objectName;
var count:int = times.length/timesStride;
for (var i:int = 0; i < count; i++) {
var index:int = i*valuesStride;
var time:Number = times[i*timesStride];
xTrack.addKey(time, values[index]);
yTrack.addKey(time, values[index + 1]);
zTrack.addKey(time, values[index + 2]);
}
return Vector.<Track>([xTrack, yTrack, zTrack]);
// TODO:: Exceptions with indices
}
return null;
}
}
}

View File

@@ -0,0 +1,164 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
/**
* @private
*/
public class DaeSource extends DaeElement {
use namespace collada;
/**
* Types of arrays.
*/
private const FLOAT_ARRAY:String = "float_array";
private const INT_ARRAY:String = "int_array";
private const NAME_ARRAY:String = "Name_array";
/**
* Array of Number items.
* Call <code>parse()</code> before using.
*/
public var numbers:Vector.<Number>;
/**
* Array of int items.
* Call <code>parse()</code> before using.
*/
public var ints:Vector.<int>;
/**
* Array of string items.
* Call <code>parse()</code> before using.
*/
public var names:Vector.<String>;
/**
* Size in bytes of one number or int element
* Call <code>parse()</code> before using.
*/
public var stride:int;
public function DaeSource(data:XML, document:DaeDocument) {
super(data, document);
// array declares within in <source>.
constructArrays();
}
private function constructArrays():void {
var children:XMLList = data.children();
for (var i:int = 0, count:int = children.length(); i < count; i++) {
var child:XML = children[i];
switch (child.localName()) {
case FLOAT_ARRAY :
case INT_ARRAY :
case NAME_ARRAY :
var array:DaeArray = new DaeArray(child, document);
if (array.id != null) {
document.arrays[array.id] = array;
}
break;
}
}
}
private function get accessor():XML {
return data.technique_common.accessor[0];
}
override protected function parseImplementation():Boolean {
var accessor:XML = this.accessor;
if (accessor != null) {
var arrayXML:XML = accessor.@source[0];
var array:DaeArray = (arrayXML == null) ? null : document.findArray(arrayXML);
if (array != null) {
var countXML:String = accessor.@count[0];
if (countXML != null) {
var count:int = parseInt(countXML.toString(), 10);
var offsetXML:XML = accessor.@offset[0];
var strideXML:XML = accessor.@stride[0];
var offset:int = (offsetXML == null) ? 0 : parseInt(offsetXML.toString(), 10);
var stride:int = (strideXML == null) ? 1 : parseInt(strideXML.toString(), 10);
array.parse();
if (array.array.length < (offset + (count*stride))) {
document.logger.logNotEnoughDataError(accessor);
return false;
}
this.stride = parseArray(offset, count, stride, array.array, array.type);
return true;
}
} else {
document.logger.logNotFoundError(arrayXML);
}
}
return false;
}
private function numValidParams(params:XMLList):int {
var res:int = 0;
for (var i:int = 0, count:int = params.length(); i < count; i++) {
if (params[i].@name[0] != null) {
res++;
}
}
return res;
}
private function parseArray(offset:int, count:int, stride:int, array:Array, type:String):int {
var params:XMLList = this.accessor.param;
var arrStride:int = Math.max(numValidParams(params), stride);
switch (type) {
case FLOAT_ARRAY:
numbers = new Vector.<Number>(int(arrStride*count));
break;
case INT_ARRAY:
ints = new Vector.<int>(int(arrStride*count));
break;
case NAME_ARRAY:
names = new Vector.<String>(int(arrStride*count));
break;
}
var curr:int = 0;
for (var i:int = 0; i < arrStride; i++) {
// Only parameters which have name field should be read
var param:XML = params[i];
if (param == null || param.hasOwnProperty("@name")) {
var j:int;
switch (type) {
case FLOAT_ARRAY:
for (j = 0; j < count; j++) {
var value:String = array[int(offset + stride*j + i)];
if (value.indexOf(",") != -1) {
value = value.replace(/,/, ".");
}
numbers[int(arrStride*j + curr)] = parseFloat(value);
}
break;
case INT_ARRAY:
for (j = 0; j < count; j++) {
ints[int(arrStride*j + curr)] = parseInt(array[int(offset + stride*j + i)], 10);
}
break;
case NAME_ARRAY:
for (j = 0; j < count; j++) {
names[int(arrStride*j + curr)] = array[int(offset + stride*j + i)];
}
break;
}
curr++;
}
}
return arrStride;
}
}
}

View File

@@ -0,0 +1,26 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
/**
* @private
*/
public class DaeUnits {
public static const METERS:Number = 1;
public static const DECIMETERS:Number = 0.1;
public static const CENTIMETERS:Number = 0.01;
public static const MILIMETERS:Number = 0.001;
public static const KILOMETERS:Number = 1000;
public static const INCHES:Number = 0.0254;
public static const FEET:Number = 0.3048;
public static const YARDS:Number = 0.9144;
public static const MILES:Number = 1609.347219;
}
}

View File

@@ -0,0 +1,76 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
import flash.geom.Vector3D;
/**
* @private
*/
public class DaeVertex {
public var vertexInIndex:int;
public var vertexOutIndex:int;
public var indices:Vector.<int> = new Vector.<int>();
public var x:Number;
public var y:Number;
public var z:Number;
public var uvs:Vector.<Number> = new Vector.<Number>();
public var normal:Vector3D;
public var tangent:Vector3D;
public function addPosition(data:Vector.<Number>, dataIndex:int, stride:int, unitScaleFactor:Number):void {
indices.push(dataIndex);
var offset:int = stride*dataIndex;
x = data[int(offset)]*unitScaleFactor;
y = data[int(offset + 1)]*unitScaleFactor;
z = data[int(offset + 2)]*unitScaleFactor;
}
public function addNormal(data:Vector.<Number>, dataIndex:int, stride:int):void {
indices.push(dataIndex);
var offset:int = stride*dataIndex;
normal = new Vector3D();
normal.x = data[int(offset++)];
normal.y = data[int(offset++)];
normal.z = data[offset];
}
public function addTangentBiDirection(tangentData:Vector.<Number>, tangentDataIndex:int, tangentStride:int, biNormalData:Vector.<Number>, biNormalDataIndex:int, biNormalStride:int):void {
indices.push(tangentDataIndex);
indices.push(biNormalDataIndex);
var tangentOffset:int = tangentStride*tangentDataIndex;
var biNormalOffset:int = biNormalStride*biNormalDataIndex;
var biNormalX:Number = biNormalData[int(biNormalOffset++)];
var biNormalY:Number = biNormalData[int(biNormalOffset++)];
var biNormalZ:Number = biNormalData[biNormalOffset];
tangent = new Vector3D(tangentData[int(tangentOffset++)], tangentData[int(tangentOffset++)], tangentData[tangentOffset]);
var crossX:Number = normal.y*tangent.z - normal.z*tangent.y;
var crossY:Number = normal.z*tangent.x - normal.x*tangent.z;
var crossZ:Number = normal.x*tangent.y - normal.y*tangent.x;
var dot:Number = crossX*biNormalX + crossY*biNormalY + crossZ*biNormalZ;
tangent.w = dot < 0 ? -1 : 1;
}
public function appendUV(data:Vector.<Number>, dataIndex:int, stride:int):void {
indices.push(dataIndex);
uvs.push(data[int(dataIndex*stride)]);
uvs.push(1 - data[int(dataIndex*stride + 1)]);
}
}
}

View File

@@ -0,0 +1,29 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
/**
* @private
*/
public class DaeVertexChannels {
public var uvChannels:Vector.<int> = new Vector.<int>();
public var normalIndex:int;
public var tangentIndex:int;
public var biNormalIndex:int;
public function DaeVertexChannels() {
}
}
}

View File

@@ -0,0 +1,49 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.alternativa3d;
use namespace alternativa3d;
/**
* @private
*/
public class DaeVertices extends DaeElement {
use namespace collada;
/**
* Source of vertex coordinates data. Stores coordinates in <code>numbers</code> array.
*<code>stride</code> property of source is not less than three.
* Call <code>parse()</code> before using.
*/
public var positions:DaeSource;
//private var texCoords:Vector.<DaeSource>;
public function DaeVertices(data:XML, document:DaeDocument) {
super(data, document);
}
override protected function parseImplementation():Boolean {
// Get array of vertex coordinates.
var inputXML:XML = data.input.(@semantic == "POSITION")[0];
if (inputXML != null) {
positions = (new DaeInput(inputXML, document)).prepareSource(3);
if (positions != null) {
return true;
}
}
return false;
}
}
}

View File

@@ -0,0 +1,43 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
/**
* @private
*/
public class DaeVisualScene extends DaeElement {
use namespace collada;
public var nodes:Vector.<DaeNode>;
public function DaeVisualScene(data:XML, document:DaeDocument) {
super(data, document);
// nodes are declared in <visual_scene>.
constructNodes();
}
public function constructNodes():void {
var nodesList:XMLList = data.node;
var count:int = nodesList.length();
nodes = new Vector.<DaeNode>(count);
for (var i:int = 0; i < count; i++) {
var node:DaeNode = new DaeNode(nodesList[i], document, this);
if (node.id != null) {
document.nodes[node.id] = node;
}
nodes[i] = node;
}
}
}
}

View File

@@ -0,0 +1,17 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.collada {
/**
* @private
*/
public namespace collada = "http://www.collada.org/2005/11/COLLADASchema";
}

View File

@@ -0,0 +1,69 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.loaders.events {
import alternativa.engine3d.resources.ExternalTextureResource;
import flash.display.BitmapData;
import flash.events.Event;
/**
* The Event is dispatched when <code>TexturesLoader</code> complete loading of textures.
*
* @see alternativa.engine3d.loaders.TexturesLoader
*/
public class TexturesLoaderEvent extends Event {
/**
* Value for <code>type</code> property .
*/
public static const COMPLETE:String = "complete";
private var bitmapDatas:Vector.<BitmapData>;
private var textures:Vector.<ExternalTextureResource>;
public function TexturesLoaderEvent(type:String, bitmapDatas:Vector.<BitmapData>, textures:Vector.<ExternalTextureResource>) {
this.bitmapDatas = bitmapDatas;
this.textures = textures;
super(type, false, false);
}
/**
* Returns the list of loaded images. Method returns the list only when method <code>TexturesLoader.loadResource()</code> is called
* and <code>TexturesLoader.loadResources()</code> with <code>needBitmapData</code> is set to <code>true</code>.
*
* @see alternativa.engine3d.loaders.TexturesLoader#loadResource()
* @see alternativa.engine3d.loaders.TexturesLoader#loadResources()
*/
public function getBitmapDatas():Vector.<BitmapData> {
return bitmapDatas;
}
/**
* Returns the list of loaded textures. Method returns the list only when method <code>TexturesLoader.loadResource()</code> is called
* and <code>TexturesLoader.loadResources()</code> with <code>needBitmapData</code> is set to <code>true</code>.
*
* @see alternativa.engine3d.loaders.TexturesLoader#loadResource()
* @see alternativa.engine3d.loaders.TexturesLoader#loadResources()
* @see alternativa.engine3d.resources.ExternalTextureResource
*/
public function getTextures():Vector.<ExternalTextureResource> {
return textures;
}
/**
* Returns copy of object.
*/
override public function clone():Event {
return new TexturesLoaderEvent(type, bitmapDatas, textures);
}
}
}

View File

@@ -0,0 +1,343 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.materials {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.materials.compiler.CommandType;
import alternativa.engine3d.materials.compiler.VariableType;
import avmplus.getQualifiedSuperclassName;
import flash.display3D.Context3D;
import flash.display3D.Context3DTextureFormat;
import flash.display3D.IndexBuffer3D;
import flash.display3D.VertexBuffer3D;
import flash.display3D.textures.Texture;
import flash.geom.Point;
import flash.utils.ByteArray;
import flash.utils.Dictionary;
import flash.utils.Endian;
import flash.utils.getDefinitionByName;
/**
* @private
*/
public class A3DUtils {
public static const NONE:int = 0;
public static const DXT1:int = 1;
public static const ETC1:int = 2;
public static const PVRTC:int = 3;
private static const DXT1Data:ByteArray = getDXT1();
private static const PVRTCData:ByteArray = getPVRTC();
private static const ETC1Data:ByteArray = getETC1();
private static function getDXT1():ByteArray {
var DXT1Data:Vector.<int> = Vector.<int>([65,84,70,0,2,71,2,2,2,3,0,0,12,0,0,0,16,0,0,85,105,56,0,0,0,0,0,157,73,73,188,1,8,0,0,0,5,0,1,188,1,0,16,0,0,0,74,0,0,0,128,188,4,0,1,0,0,0,1,0,0,0,129,188,4,0,1,0,0,0,2,0,0,0,192,188,4,0,1,0,0,0,90,0,0,0,193,188,4,0,1,0,0,0,66,0,0,0,0,0,0,0,36,195,221,111,3,78,254,75,177,133,61,119,118,141,201,10,87,77,80,72,79,84,79,0,25,0,192,122,0,0,0,1,96,0,160,0,10,0,0,160,0,0,0,4,111,255,0,1,0,0,1,0,224,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,16,0,0,85,105,56,0,0,0,0,0,157,73,73,188,1,8,0,0,0,5,0,1,188,1,0,16,0,0,0,74,0,0,0,128,188,4,0,1,0,0,0,1,0,0,0,129,188,4,0,1,0,0,0,2,0,0,0,192,188,4,0,1,0,0,0,90,0,0,0,193,188,4,0,1,0,0,0,66,0,0,0,0,0,0,0,36,195,221,111,3,78,254,75,177,133,61,119,118,141,201,10,87,77,80,72,79,84,79,0,25,0,192,122,0,0,0,1,96,0,160,0,10,0,0,160,0,0,0,4,111,255,0,1,0,0,1,0,224,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,114,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,16,0,0,85,105,56,0,0,0,0,0,157,73,73,188,1,8,0,0,0,5,0,1,188,1,0,16,0,0,0,74,0,0,0,128,188,4,0,1,0,0,0,1,0,0,0,129,188,4,0,1,0,0,0,2,0,0,0,192,188,4,0,1,0,0,0,90,0,0,0,193,188,4,0,1,0,0,0,66,0,0,0,0,0,0,0,36,195,221,111,3,78,254,75,177,133,61,119,118,141,201,10,87,77,80,72,79,84,79,0,25,0,192,122,0,0,0,1,96,0,160,0,10,0,0,160,0,0,0,4,111,255,0,1,0,0,1,0,224,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,114,0,7,143,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
return getData(DXT1Data);
}
private static function getETC1():ByteArray {
var ETC1Data:Vector.<int> = Vector.<int>([65,84,70,0,2,104,2,2,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,16,0,0,0,255,252,0,0,0,0,12,0,0,0,16,0,0,127,233,56,0,0,0,0,0,157,73,73,188,1,8,0,0,0,5,0,1,188,1,0,16,0,0,0,74,0,0,0,128,188,4,0,1,0,0,0,1,0,0,0,129,188,4,0,1,0,0,0,2,0,0,0,192,188,4,0,1,0,0,0,90,0,0,0,193,188,4,0,1,0,0,0,66,0,0,0,0,0,0,0,36,195,221,111,3,78,254,75,177,133,61,119,118,141,201,9,87,77,80,72,79,84,79,0,25,0,192,120,0,0,0,1,96,0,160,0,10,0,0,160,0,0,0,4,111,255,0,1,0,0,1,0,208,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,114,0,7,143,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,16,0,0,0,255,252,0,0,0,0,12,0,0,0,16,0,0,127,233,56,0,0,0,0,0,157,73,73,188,1,8,0,0,0,5,0,1,188,1,0,16,0,0,0,74,0,0,0,128,188,4,0,1,0,0,0,1,0,0,0,129,188,4,0,1,0,0,0,2,0,0,0,192,188,4,0,1,0,0,0,90,0,0,0,193,188,4,0,1,0,0,0,66,0,0,0,0,0,0,0,36,195,221,111,3,78,254,75,177,133,61,119,118,141,201,9,87,77,80,72,79,84,79,0,25,0,192,120,0,0,0,1,96,0,160,0,10,0,0,160,0,0,0,4,111,255,0,1,0,0,1,0,208,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,114,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,16,0,0,0,255,252,0,0,0,0,12,0,0,0,16,0,0,127,233,56,0,0,0,0,0,157,73,73,188,1,8,0,0,0,5,0,1,188,1,0,16,0,0,0,74,0,0,0,128,188,4,0,1,0,0,0,1,0,0,0,129,188,4,0,1,0,0,0,2,0,0,0,192,188,4,0,1,0,0,0,90,0,0,0,193,188,4,0,1,0,0,0,66,0,0,0,0,0,0,0,36,195,221,111,3,78,254,75,177,133,61,119,118,141,201,9,87,77,80,72,79,84,79,0,25,0,192,120,0,0,0,1,96,0,160,0,10,0,0,160,0,0,0,4,111,255,0,1,0,0,1,0,208,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,114,0,4,0]);
return getData(ETC1Data);
}
private static function getPVRTC():ByteArray {
var PVRTCData:Vector.<int> = Vector.<int>([65,84,70,0,2,173,2,2,2,3,0,0,0,0,0,0,0,0,13,0,0,0,16,0,0,0,104,190,153,255,0,0,0,0,15,91,0,0,16,0,0,102,12,228,2,255,225,0,0,0,0,0,223,73,73,188,1,8,0,0,0,5,0,1,188,1,0,16,0,0,0,74,0,0,0,128,188,4,0,1,0,0,0,2,0,0,0,129,188,4,0,1,0,0,0,4,0,0,0,192,188,4,0,1,0,0,0,90,0,0,0,193,188,4,0,1,0,0,0,132,0,0,0,0,0,0,0,36,195,221,111,3,78,254,75,177,133,61,119,118,141,201,9,87,77,80,72,79,84,79,0,25,0,192,120,0,1,0,3,96,0,160,0,10,0,0,160,0,0,0,4,111,255,0,1,0,0,1,0,165,192,0,7,227,99,186,53,197,40,185,134,182,32,130,98,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,143,192,120,64,6,16,34,52,192,196,65,132,90,98,68,16,17,68,60,91,8,48,76,35,192,97,132,71,76,33,164,97,1,2,194,12,19,8,240,29,132,24,38,17,224,48,194,35,166,16,210,48,128,128,24,68,121,132,52,204,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,16,0,0,0,233,56,90,0,0,0,0,12,0,0,0,16,0,0,127,237,210,0,0,0,0,0,155,73,73,188,1,8,0,0,0,5,0,1,188,1,0,16,0,0,0,74,0,0,0,128,188,4,0,1,0,0,0,2,0,0,0,129,188,4,0,1,0,0,0,4,0,0,0,192,188,4,0,1,0,0,0,90,0,0,0,193,188,4,0,1,0,0,0,64,0,0,0,0,0,0,0,36,195,221,111,3,78,254,75,177,133,61,119,118,141,201,9,87,77,80,72,79,84,79,0,25,0,192,120,0,1,0,3,96,0,160,0,10,0,0,160,0,0,0,4,111,255,0,1,0,0,1,0,188,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,16,0,0,0,233,56,90,0,0,0,0,12,0,0,0,16,0,0,127,237,210,0,0,0,0,0,155,73,73,188,1,8,0,0,0,5,0,1,188,1,0,16,0,0,0,74,0,0,0,128,188,4,0,1,0,0,0,2,0,0,0,129,188,4,0,1,0,0,0,4,0,0,0,192,188,4,0,1,0,0,0,90,0,0,0,193,188,4,0,1,0,0,0,64,0,0,0,0,0,0,0,36,195,221,111,3,78,254,75,177,133,61,119,118,141,201,9,87,77,80,72,79,84,79,0,25,0,192,120,0,1,0,3,96,0,160,0,10,0,0,160,0,0,0,4,111,255,0,1,0,0,1,0,188,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,200,0,0,0,0,0,0,0,0,0,0]);
return getData(PVRTCData);
}
private static function getData(source:Vector.<int>):ByteArray {
var result:ByteArray = new ByteArray();
for (var i:int = 0, length:int = source.length; i < length; i++) {
result.writeByte(source[i]);
}
return result;
}
public static function getSizeFromATF(byteArray:ByteArray, size:Point):void {
byteArray.position = 7;
var w:int = byteArray.readByte();
var h:int = byteArray.readByte();
size.x = 1 << w;
size.y = 1 << h;
byteArray.position = 0;
}
public static function getSupportedTextureFormat(context3D:Context3D):int {
var testTexture:Texture = context3D.createTexture(4, 4, Context3DTextureFormat.COMPRESSED, false);
var result:int = NONE;
try {
testTexture.uploadCompressedTextureFromByteArray(DXT1Data, 0);
result = DXT1;
} catch(e:Error) {
result = NONE;
}
if (result == NONE) {
try {
testTexture.uploadCompressedTextureFromByteArray(PVRTCData, 0);
result = PVRTC;
} catch(e:Error) {
result = NONE;
}
}
if (result == NONE) {
try {
testTexture.uploadCompressedTextureFromByteArray(ETC1Data, 0);
result = ETC1;
} catch(e:Error) {
result = NONE;
}
}
testTexture.dispose();
return result;
}
public static function vectorNumberToByteArray(vector:Vector.<Number>):ByteArray {
var result:ByteArray = new ByteArray();
result.endian = Endian.LITTLE_ENDIAN;
for (var i:int = 0; i < vector.length; i++) {
result.writeFloat(vector[i]);
}
result.position = 0;
return result;
}
public static function byteArrayToVectorUint(byteArray:ByteArray):Vector.<uint> {
var result:Vector.<uint> = new Vector.<uint>();
var length:uint = 0;
byteArray.position = 0;
byteArray.endian = Endian.LITTLE_ENDIAN;
while (byteArray.bytesAvailable > 0) {
result[length++] = byteArray.readUnsignedShort();
}
return result;
}
public static function createVertexBufferFromByteArray(context:Context3D, byteArray:ByteArray, numVertices:uint, stride:uint = 3):VertexBuffer3D {
if (context == null) {
throw new ReferenceError("context is not set");
}
var buffer:VertexBuffer3D = context.createVertexBuffer(numVertices, stride);
buffer.uploadFromByteArray(byteArray, 0, 0, numVertices);
return buffer;
}
public static function createVertexBufferFromVector(context:Context3D, vector:Vector.<Number>, numVertices:uint, stride:uint = 3):VertexBuffer3D {
if (context == null) {
throw new ReferenceError("context is not set");
}
var buffer:VertexBuffer3D = context.createVertexBuffer(numVertices, stride);
var byteArray:ByteArray = A3DUtils.vectorNumberToByteArray(vector);
buffer.uploadFromByteArray(byteArray, 0, 0, numVertices);
return buffer;
}
public static function createTextureFromByteArray(context:Context3D, byteArray:ByteArray, width:Number, height:Number, format:String):Texture {
if (context == null) {
throw new ReferenceError("context is not set");
}
var texture:Texture = context.createTexture(width, height, format, false);
texture.uploadCompressedTextureFromByteArray(byteArray, 0);
return texture;
}
public static function createIndexBufferFromByteArray(context:Context3D, byteArray:ByteArray, numIndices:uint):IndexBuffer3D {
if (context == null) {
throw new ReferenceError("context is not set");
}
var buffer:IndexBuffer3D = context.createIndexBuffer(numIndices);
buffer.uploadFromByteArray(byteArray, 0, 0, numIndices);
return buffer;
}
public static function createIndexBufferFromVector(context:Context3D, vector:Vector.<uint>, numIndices:int = -1):IndexBuffer3D {
if (context == null) {
throw new ReferenceError("context is not set");
}
var count:uint = numIndices > 0 ? numIndices : vector.length;
var buffer:IndexBuffer3D = context.createIndexBuffer(count);
buffer.uploadFromVector(vector, 0, count);
var byteArray:ByteArray = new ByteArray();
byteArray.endian = Endian.LITTLE_ENDIAN;
for (var i:int = 0; i < count; i++) {
byteArray.writeInt(vector[i]);
}
byteArray.position = 0;
buffer.uploadFromVector(vector, 0, count);
return buffer;
}
// Disassembler
private static var programType:Vector.<String> = Vector.<String>(["VERTEX", "FRAGMENT"]);
private static var samplerDimension:Vector.<String> = Vector.<String>(["2D", "cube", "3D"]);
private static var samplerWraping:Vector.<String> = Vector.<String>(["clamp", "repeat"]);
private static var samplerMipmap:Vector.<String> = Vector.<String>(["mipnone", "mipnearest", "miplinear"]);
private static var samplerFilter:Vector.<String> = Vector.<String>(["nearest", "linear"]);
private static var swizzleType:Vector.<String> = Vector.<String>(["x", "y", "z", "w"]);
private static var twoOperandsCommands:Dictionary;
private static const O_CODE:uint = "o".charCodeAt(0);
public static function disassemble(byteCode:ByteArray):String {
if (!twoOperandsCommands) {
twoOperandsCommands = new Dictionary();
twoOperandsCommands[0x1] = true;
twoOperandsCommands[0x2] = true;
twoOperandsCommands[0x3] = true;
twoOperandsCommands[0x4] = true;
twoOperandsCommands[0x6] = true;
twoOperandsCommands[0xb] = true;
twoOperandsCommands[0x11] = true;
twoOperandsCommands[0x12] = true;
twoOperandsCommands[0x13] = true;
twoOperandsCommands[0x17] = true;
twoOperandsCommands[0x18] = true;
twoOperandsCommands[0x19] = true;
twoOperandsCommands[0x28] = true;
twoOperandsCommands[0x29] = true;
twoOperandsCommands[0x2a] = true;
twoOperandsCommands[0x2c] = true;
twoOperandsCommands[0x2d] = true;
}
var res:String = "";
byteCode.position = 0;
if (byteCode.bytesAvailable < 7) {
return "error in byteCode header";
}
res += "magic = " + byteCode.readUnsignedByte().toString(16);
res += "\nversion = " + byteCode.readInt().toString(10);
res += "\nshadertypeid = " + byteCode.readUnsignedByte().toString(16);
var pType:String = programType[byteCode.readByte()];
res += "\nshadertype = " + pType;
res += "\nsource\n";
pType = pType.substring(0, 1).toLowerCase();
var lineNumber:uint = 1;
while (byteCode.bytesAvailable - 24 >= 0) {
res += (lineNumber++).toString() + ": " + getCommand(byteCode, pType) + "\n";
}
if (byteCode.bytesAvailable > 0) {
res += "\nunexpected byteCode length. extra bytes:" + byteCode.bytesAvailable;
}
return res;
}
private static function getCommand(byteCode:ByteArray, programType:String):String {
var cmd:uint = byteCode.readUnsignedInt();
var command:String = CommandType.COMMAND_NAMES[cmd];
var result:String;
var destNumber:uint = byteCode.readUnsignedShort();
var swizzle:uint = byteCode.readByte();
var s:String = "";
var destSwizzle:uint = 0;
if (swizzle < 15) {
s += ".";
s += ((swizzle & 0x1) > 0) ? "x" : "";
s += ((swizzle & 0x2) > 0) ? "y" : "";
s += ((swizzle & 0x4) > 0) ? "z" : "";
s += ((swizzle & 0x8) > 0) ? "w" : "";
destSwizzle = s.length;
}
var destType:String = VariableType.TYPE_NAMES[byteCode.readUnsignedByte()].charAt(0);
if (destType.charCodeAt(0) == O_CODE) {
result = command + " " + attachProgramPrefix(destType, programType) + s + ", ";
} else {
result = command + " " + attachProgramPrefix(destType, programType) + destNumber.toString() + s + ", ";
}
result += attachProgramPrefix(getSourceVariable(byteCode, destSwizzle), programType);
if (twoOperandsCommands[cmd]) {
if (cmd == 0x28) {
result += ", " + attachProgramPrefix(getSamplerVariable(byteCode), programType);
}
else {
result += ", " + attachProgramPrefix(getSourceVariable(byteCode, destSwizzle), programType);
}
}
else {
byteCode.readDouble();
}
return result;
}
private static function attachProgramPrefix(variable:String, programType:String):String {
var char:uint = variable.charCodeAt(0);
if (char == "o".charCodeAt(0))
return variable + (programType == "f" ? "c" : "p");
else if (char != "v".charCodeAt(0))
return programType + variable;
return variable;
}
private static function getSamplerVariable(byteCode:ByteArray):String {
var number:uint = byteCode.readUnsignedInt();
byteCode.readByte();
var dim:uint = byteCode.readByte() >> 4;
var wraping:uint = byteCode.readByte() >> 4;
var n:uint = byteCode.readByte();
return "s" + number.toString() + " <" + samplerDimension[dim] + ", " + samplerWraping[wraping]
+ ", " + samplerFilter[(n >> 4) & 0xf] + ", " + samplerMipmap[n & 0xf] + ">";
}
private static function getSourceVariable(byteCode:ByteArray, destSwizzle:uint):String {
var s1Number:uint = byteCode.readUnsignedShort();
var offset:uint = byteCode.readUnsignedByte();
var s:String = getSourceSwizzle(byteCode.readUnsignedByte(), destSwizzle);
var s1Type:String = VariableType.TYPE_NAMES[byteCode.readUnsignedByte()].charAt(0);
var indexType:String = VariableType.TYPE_NAMES[byteCode.readUnsignedByte()].charAt(0);
var comp:String = swizzleType[byteCode.readUnsignedByte()];
if (byteCode.readUnsignedByte() > 0) {
return s1Type + "[" + indexType + s1Number.toString() + "." + comp + ((offset > 0) ? ("+" + offset.toString()) : "") + "]" + s;
}
return s1Type + s1Number.toString() + s;
}
private static function getSourceSwizzle(swizzle:uint, destSwizzle:uint):String {
var s:String = "";
if (swizzle != 0xe4) {
s += ".";
s += swizzleType[(swizzle & 0x3)];
s += swizzleType[(swizzle >> 2) & 0x3];
s += swizzleType[(swizzle >> 4) & 0x3];
s += swizzleType[(swizzle >> 6) & 0x3];
s = s.substring(0, destSwizzle > 0 ? destSwizzle : s.length);
}
return s;
}
alternativa3d static function checkParent(child:Class, parent:Class):Boolean {
var current:Class = child;
if (parent == null) return true;
while (true) {
if (current == parent) return true;
var className:String = getQualifiedSuperclassName(current);
if (className != null) {
current = getDefinitionByName(className) as Class;
} else return false;
}
return false;
}
}
}

View File

@@ -0,0 +1,922 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.materials {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.DrawUnit;
import alternativa.engine3d.core.Light3D;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Renderer;
import alternativa.engine3d.core.Transform3D;
import alternativa.engine3d.core.VertexAttributes;
import alternativa.engine3d.materials.compiler.Linker;
import alternativa.engine3d.materials.compiler.Procedure;
import alternativa.engine3d.materials.compiler.VariableType;
import alternativa.engine3d.objects.Surface;
import alternativa.engine3d.resources.BitmapTextureResource;
import alternativa.engine3d.resources.Geometry;
import alternativa.engine3d.resources.TextureResource;
import avmplus.getQualifiedClassName;
import flash.display.BitmapData;
import flash.display3D.Context3D;
import flash.display3D.Context3DBlendFactor;
import flash.display3D.Context3DProgramType;
import flash.display3D.VertexBuffer3D;
import flash.display3D.textures.CubeTexture;
import flash.utils.Dictionary;
import flash.utils.getDefinitionByName;
use namespace alternativa3d;
/**
* The material which reflects the environment given with cube texture.
*
* @see alternativa.engine3d.resources.BitmapCubeTextureResource
* @see alternativa.engine3d.resources.ExternalTextureResource
*/
public class EnvironmentMaterial extends TextureMaterial {
private static var caches:Dictionary = new Dictionary(true);
private var cachedContext3D:Context3D;
private var programsCache:Array;
/**
* @private
*/
alternativa3d static var fogMode:int = FogMode.DISABLED;
/**
* @private
*/
alternativa3d static var fogNear:Number = 1000;
/**
* @private
*/
alternativa3d static var fogFar:Number = 5000;
/**
* @private
*/
alternativa3d static var fogMaxDensity:Number = 1;
/**
* @private
*/
alternativa3d static var fogColorR:Number = 0xC8/255;
/**
* @private
*/
alternativa3d static var fogColorG:Number = 0xA2/255;
/**
* @private
*/
alternativa3d static var fogColorB:Number = 0xC8/255;
/**
* @private
*/
alternativa3d static var fogTexture:TextureResource;
/**
* @private
*/
static alternativa3d const _passReflectionProcedure:Procedure = new Procedure([
// i0 = position, i1 = normal
"#v1=vNormal",
"#v0=vPosition",
"mov v0, i0",
"mov v1, i1"
], "passReflectionProcedure");
/**
* @private
*/
static alternativa3d const _applyReflectionProcedure:Procedure = getApplyReflectionProcedure();
private static function getApplyReflectionProcedure():Procedure {
var result:Procedure = new Procedure([
"#v1=vNormal",
"#v0=vPosition",
"#s0=sCubeMap",
"#c0=cCamera",
"sub t0, v0, c0",
"dp3 t1.x, v1, t0",
"add t1.x, t1.x, t1.x",
"mul t1, v1, t1.x",
"sub t1, t0, t1",
"nrm t1.xyz, t1.xyz",
"m33 t1.xyz, t1.xyz, c1",
"nrm t1.xyz, t1.xyz",
"tex o0, t1, s0 <cube,clamp,linear,nomip>"
], "applyReflectionProcedure");
result.assignVariableName(VariableType.CONSTANT, 1, "cLocalToGlobal", 3);
return result;
}
/**
* @private
*/
static alternativa3d const _applyReflectionNormalMapProcedure:Procedure = getApplyReflectionNormalMapProcedure();
private static function getApplyReflectionNormalMapProcedure():Procedure {
var result:Procedure = new Procedure([
"#s0=sCubeMap",
"#c0=cCamera",
"#v0=vPosition",
"sub t0, v0, c0",
"dp3 t1.x, i0.xyz, t0",
"add t1.x, t1.x, t1.x",
"mul t1, i0.xyz, t1.x",
"sub t1, t0, t1",
"nrm t1.xyz, t1.xyz",
"m33 t1.xyz, t1.xyz, c1",
"nrm t1.xyz, t1.xyz",
"tex o0, t1, s0 <cube,clamp,linear,nomip>"
], "applyReflectionNormalMapProcedure");
result.assignVariableName(VariableType.CONSTANT, 1, "cLocalToGlobal", 3);
return result;
}
/**
* @private
*/
static alternativa3d const _blendReflection:Procedure = new Procedure([
"#c0=cAlpha",
"mul t1.xyz, i0.xyz, c0.y",
"mul t0.xyz, i1, c0.z",
"add t0.xyz, t1.xyz, t0",
"mov t0.w, i0.w",
"mov o0, t0"
], "blendReflection");
/**
* @private
*/
static alternativa3d const _blendReflectionMap:Procedure = new Procedure([
"#c0=cCamera",
"#c1=cAlpha",
"#s0=sReflection",
"#v0=vUV",
"tex t0, v0, s0 <2d,repeat,linear,miplinear>",
"mul t0, t0, c1.z",
"mul t1.xyz, i1, t0",
"sub t0, c0.www, t0",
"mul t2, i0, t0",
"add t0.xyz, t1, t2",
"mov t0.w, i0.w",
"mov o0, t0"
], "blendReflectionMap");
// inputs : tangent, normal
private static const _passTBNRightProcedure:Procedure = getPassTBNProcedure(true);
private static const _passTBNLeftProcedure:Procedure = getPassTBNProcedure(false);
private static function getPassTBNProcedure(right:Boolean):Procedure {
var crsInSpace:String = (right) ? "crs t1.xyz, i0, i1" : "crs t1.xyz, i1, i0";
return new Procedure([
"#v0=vTangent",
"#v1=vBinormal",
"#v2=vNormal",
// Calculates binormal
crsInSpace,
"mul t1.xyz, t1.xyz, i0.w",
// Transpose normal matrix
"mov v0.x, i0.x",
"mov v0.y, t1.x",
"mov v0.z, i1.x",
"mov v0.w, i1.w",
"mov v1.x, i0.y",
"mov v1.y, t1.y",
"mov v1.z, i1.y",
"mov v1.w, i1.w",
"mov v2.x, i0.z",
"mov v2.y, t1.z",
"mov v2.z, i1.z",
"mov v2.w, i1.w"
], "passTBNProcedure");
}
// outputs : normal, viewVector
private static const _getNormalTangentProcedure:Procedure = new Procedure([
"#v0=vTangent",
"#v1=vBinormal",
"#v2=vNormal",
"#v3=vUV",
"#c0=cCamera",
"#s0=sBump",
// Extract normal from the texture
"tex t0, v3, s0 <2d,repeat,linear,miplinear>",
"add t0, t0, t0",
"sub t0.xyz, t0.xyz, c0.www",
// Transform the normal with TBN
"nrm t1.xyz, v0.xyz",
"dp3 o0.x, t0.xyz, t1.xyz",
"nrm t1.xyz, v1.xyz",
"dp3 o0.y, t0.xyz, t1.xyz",
"nrm t1.xyz, v2.xyz",
"dp3 o0.z, t0.xyz, t1.xyz",
// Normalization after transform
"nrm o0.xyz, o0.xyz"
], "getNormalTangentProcedure");
// outputs : normal, viewVector
private static const _getNormalObjectProcedure:Procedure = new Procedure([
"#v3=vUV",
"#c0=cCamera",
"#s0=sBump",
// Extract normal from the texture
"tex t0, v3, s0 <2d,repeat,linear,miplinear>",
"add t0, t0, t0",
"sub t0.xyz, t0.xyz, c0.www",
// Normalization
"nrm o0.xyz, t0.xyz"
], "getNormalObjectProcedure");
// inputs : position
private static const passSimpleFogConstProcedure:Procedure = new Procedure([
"#v0=vZDistance",
"#c0=cFogSpace",
"dp4 t0.z, i0, c0",
"mov v0, t0.zzzz",
"sub v0.y, i0.w, t0.z"
], "passSimpleFogConst");
// inputs : color
private static const outputWithSimpleFogProcedure:Procedure = new Procedure([
"#v0=vZDistance",
"#c0=cFogColor",
"#c1=cFogRange",
// Restrict fog factor with the range
"min t0.xy, v0.xy, c1.xy",
"max t0.xy, t0.xy, c1.zw",
"mul i0.xyz, i0.xyz, t0.y",
"mul t0.xyz, c0.xyz, t0.x",
"add i0.xyz, i0.xyz, t0.xyz",
"mov o0, i0"
], "outputWithSimpleFog");
// inputs : position, projected
private static const postPassAdvancedFogConstProcedure:Procedure = new Procedure([
"#v0=vZDistance",
"#c0=cFogSpace",
"dp4 t0.z, i0, c0",
"mov v0, t0.zzzz",
"sub v0.y, i0.w, t0.z",
// Screen x coordinate
"mov v0.zw, i1.xwxw",
"mov o0, i1"
], "postPassAdvancedFogConst");
// inputs : color
private static const outputWithAdvancedFogProcedure:Procedure = new Procedure([
"#v0=vZDistance",
"#c0=cFogConsts",
"#c1=cFogRange",
"#s0=sFogTexture",
// Restrict fog factor with the range
"min t0.xy, v0.xy, c1.xy",
"max t0.xy, t0.xy, c1.zw",
"mul i0.xyz, i0.xyz, t0.y",
// Calculate fog color
"mov t1.xyzw, c0.yyzw",
"div t0.z, v0.z, v0.w",
"mul t0.z, t0.z, c0.x",
"add t1.x, t1.x, t0.z",
"tex t1, t1, s0 <2d,repeat,linear,miplinear>",
"mul t0.xyz, t1.xyz, t0.x",
"add i0.xyz, i0.xyz, t0.xyz",
"mov o0, i0"
], "outputWithAdvancedFog");
private static const _applyLightMapProcedure:Procedure = new Procedure([
"#v0=vUV1",
"#s0=sLightMap",
"tex t0, v0, s0 <2d,repeat,linear,miplinear>",
"add t0, t0, t0",
"mul o0.xyz, i0.xyz, t0.xyz"
], "applyLightMapProcedure");
private static const _passLightMapUVProcedure:Procedure = new Procedure([
"#a0=aUV1",
"#v0=vUV1",
"mov v0, a0"
], "passLightMapUVProcedure");
private var _normalMapSpace:int = NormalMapSpace.TANGENT_RIGHT_HANDED;
/**
* Type of the normal map. Should be defined by constants of <code>NormalMapSpace</code> class.
* @default NormalMapSpace.TANGENT
*
* @see NormalMapSpace
*/
public function get normalMapSpace():int {
return _normalMapSpace;
}
/**
* @private
*/
public function set normalMapSpace(value:int):void {
if (value != NormalMapSpace.TANGENT_RIGHT_HANDED && value != NormalMapSpace.TANGENT_LEFT_HANDED && value != NormalMapSpace.OBJECT) {
throw new ArgumentError("Value must be a constant from the NormalMapSpace class");
}
_normalMapSpace = value;
dirty();
}
/**
* Normal map.
*/
public function get normalMap():TextureResource {
return _normalMap;
}
/**
* @private
*/
public function set normalMap(value:TextureResource):void {
_normalMap = value;
dirty();
}
/**
* Reflection texture. Should be <code>BitmapCubeTextureResource</code> or <code>ExternalTextureResource</code> with CubeTexture data.
*/
public function get environmentMap():TextureResource {
return _environmentMap;
}
/**
* @private
*/
public function set environmentMap(value:TextureResource):void {
_environmentMap = value;
dirty();
}
/**
* Reflectivity map.
*/
public function get reflectionMap():TextureResource {
return _reflectionMap;
}
/**
* @private
*/
public function set reflectionMap(value:TextureResource):void {
_reflectionMap = value;
dirty();
}
/**
* Light map.
*/
public function get lightMap():TextureResource {
return _lightMap;
}
/**
* @private
*/
public function set lightMap(value:TextureResource):void {
_lightMap = value;
dirty();
}
/**
* Reflectivity.
*/
public var reflection:Number = 1;
/**
* Number of the UV-channel for light map.
*/
public var lightMapChannel:uint = 1;
/**
* @private
*/
alternativa3d var _normalMap:TextureResource;
/**
* @private
*/
alternativa3d var _environmentMap:TextureResource;
/**
* @private
*/
alternativa3d var _reflectionMap:TextureResource;
/**
* @private
*/
alternativa3d var _lightMap:TextureResource;
private var localToGlobalTransform:Transform3D = new Transform3D();
/*alternativa3d var lightMapOptions:SamplerOptions = new SamplerOptions(this);
alternativa3d var normalMapOptions:SamplerOptions = new SamplerOptions(this);
alternativa3d var environmentMapOptions:SamplerOptions = new SamplerOptions(this);
alternativa3d var reflectionMapOptions:SamplerOptions = new SamplerOptions(this);
alternativa3d var diffuseMapOptions:SamplerOptions = new SamplerOptions(this);*/
/**
* Creates a new EnvironmentMaterial instance.
* @param diffuseMap
* @param environmentMap
* @param normalMap
* @param reflectionMap
* @param lightMap
* @param opacityMap
* @param alpha
*/
public function EnvironmentMaterial(diffuseMap:TextureResource = null, environmentMap:TextureResource = null, normalMap:TextureResource = null, reflectionMap:TextureResource = null, lightMap:TextureResource = null, opacityMap:TextureResource = null, alpha:Number = 1) {
super(diffuseMap, opacityMap, alpha);
this._environmentMap = environmentMap;
this._normalMap = normalMap;
this._reflectionMap = reflectionMap;
this._lightMap = lightMap;
}
/**
* @inheritDoc
*/
override public function clone():Material {
var res:EnvironmentMaterial = new EnvironmentMaterial(diffuseMap, _environmentMap, _normalMap, _reflectionMap, _lightMap, opacityMap, alpha);
res.clonePropertiesFrom(this);
return res;
}
/**
* @inheritDoc
*/
override protected function clonePropertiesFrom(source:Material):void {
super.clonePropertiesFrom(source);
var eMaterial:EnvironmentMaterial = EnvironmentMaterial(source);
reflection = eMaterial.reflection;
lightMapChannel = eMaterial.lightMapChannel;
_normalMapSpace = eMaterial._normalMapSpace;
}
/**
* @private
*/
alternativa3d override function fillResources(resources:Dictionary, resourceType:Class):void {
super.alternativa3d::fillResources(resources, resourceType);
if (_environmentMap != null && A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(_environmentMap)) as Class, resourceType)) {
resources[_environmentMap] = true;
}
if (_normalMap != null && A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(_normalMap)) as Class, resourceType)) {
resources[_normalMap] = true;
}
if (_reflectionMap != null && A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(_reflectionMap)) as Class, resourceType)) {
resources[_reflectionMap] = true;
}
if (_lightMap != null && A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(_lightMap)) as Class, resourceType)) {
resources[_lightMap] = true;
}
}
private function setupProgram(targetObject:Object3D, opacityMap:TextureResource, alphaTest:int):EnvironmentMaterialShaderProgram {
var vertexLinker:Linker = new Linker(Context3DProgramType.VERTEX);
var fragmentLinker:Linker = new Linker(Context3DProgramType.FRAGMENT);
var positionVar:String = "aPosition";
var normalVar:String = "aNormal";
var tangentVar:String = "aTangent";
vertexLinker.declareVariable(positionVar, VariableType.ATTRIBUTE);
vertexLinker.declareVariable(normalVar, VariableType.ATTRIBUTE);
if (targetObject.transformProcedure != null) {
positionVar = appendPositionTransformProcedure(targetObject.transformProcedure, vertexLinker);
}
var procedure:Procedure;
if (targetObject.deltaTransformProcedure != null) {
vertexLinker.declareVariable("tTransformedNormal");
procedure = targetObject.deltaTransformProcedure.newInstance();
vertexLinker.addProcedure(procedure);
vertexLinker.setInputParams(procedure, normalVar);
vertexLinker.setOutputParams(procedure, "tTransformedNormal");
normalVar = "tTransformedNormal";
if ((_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED) && _normalMap != null) {
vertexLinker.declareVariable(tangentVar, VariableType.ATTRIBUTE);
vertexLinker.declareVariable("tTransformedTangent");
procedure = targetObject.deltaTransformProcedure.newInstance();
vertexLinker.addProcedure(procedure);
vertexLinker.setInputParams(procedure, tangentVar);
vertexLinker.setOutputParams(procedure, "tTransformedTangent");
tangentVar = "tTransformedTangent";
}
} else {
if ((_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED) && _normalMap != null) {
vertexLinker.declareVariable(tangentVar, VariableType.ATTRIBUTE);
}
}
if (_lightMap != null) {
vertexLinker.addProcedure(_passLightMapUVProcedure);
}
vertexLinker.addProcedure(_passReflectionProcedure);
vertexLinker.setInputParams(_passReflectionProcedure, positionVar, normalVar);
vertexLinker.addProcedure(_projectProcedure);
vertexLinker.setInputParams(_projectProcedure, positionVar);
vertexLinker.addProcedure(_passUVProcedure);
if (_normalMap != null) {
fragmentLinker.declareVariable("tNormal");
if (_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED) {
var nrmProcedure:Procedure = (_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED) ? _passTBNRightProcedure : _passTBNLeftProcedure;
vertexLinker.addProcedure(nrmProcedure);
vertexLinker.setInputParams(nrmProcedure, tangentVar, normalVar);
fragmentLinker.addProcedure(_getNormalTangentProcedure);
fragmentLinker.setOutputParams(_getNormalTangentProcedure, "tNormal");
} else {
fragmentLinker.addProcedure(_getNormalObjectProcedure);
fragmentLinker.setOutputParams(_getNormalObjectProcedure, "tNormal");
}
}
fragmentLinker.declareVariable("tColor");
outputProcedure = opacityMap != null ? getDiffuseOpacityProcedure : getDiffuseProcedure;
fragmentLinker.addProcedure(outputProcedure);
fragmentLinker.setOutputParams(outputProcedure, "tColor");
if (alphaTest > 0) {
outputProcedure = alphaTest == 1 ? thresholdOpaqueAlphaProcedure : thresholdTransparentAlphaProcedure;
fragmentLinker.addProcedure(outputProcedure, "tColor");
fragmentLinker.setOutputParams(outputProcedure, "tColor");
}
fragmentLinker.declareVariable("tReflection");
if (_normalMap != null) {
fragmentLinker.addProcedure(_applyReflectionNormalMapProcedure);
fragmentLinker.setInputParams(_applyReflectionNormalMapProcedure, "tNormal");
fragmentLinker.setOutputParams(_applyReflectionNormalMapProcedure, "tReflection");
} else {
fragmentLinker.addProcedure(_applyReflectionProcedure);
fragmentLinker.setOutputParams(_applyReflectionProcedure, "tReflection");
}
if (_lightMap != null) {
fragmentLinker.addProcedure(_applyLightMapProcedure);
fragmentLinker.setInputParams(_applyLightMapProcedure, "tColor");
fragmentLinker.setOutputParams(_applyLightMapProcedure, "tColor");
}
var outputProcedure:Procedure;
if (_reflectionMap != null) {
fragmentLinker.addProcedure(_blendReflectionMap);
fragmentLinker.setInputParams(_blendReflectionMap, "tColor", "tReflection");
outputProcedure = _blendReflectionMap;
} else {
fragmentLinker.addProcedure(_blendReflection);
fragmentLinker.setInputParams(_blendReflection, "tColor", "tReflection");
outputProcedure = _blendReflection;
}
if (fogMode == FogMode.SIMPLE || fogMode == FogMode.ADVANCED) {
fragmentLinker.setOutputParams(outputProcedure, "tColor");
}
if (fogMode == FogMode.SIMPLE) {
vertexLinker.addProcedure(passSimpleFogConstProcedure);
vertexLinker.setInputParams(passSimpleFogConstProcedure, positionVar);
fragmentLinker.addProcedure(outputWithSimpleFogProcedure);
fragmentLinker.setInputParams(outputWithSimpleFogProcedure, "tColor");
} else if (fogMode == FogMode.ADVANCED) {
vertexLinker.declareVariable("tProjected");
vertexLinker.setOutputParams(_projectProcedure, "tProjected");
vertexLinker.addProcedure(postPassAdvancedFogConstProcedure);
vertexLinker.setInputParams(postPassAdvancedFogConstProcedure, positionVar, "tProjected");
fragmentLinker.addProcedure(outputWithAdvancedFogProcedure);
fragmentLinker.setInputParams(outputWithAdvancedFogProcedure, "tColor");
}
fragmentLinker.varyings = vertexLinker.varyings;
return new EnvironmentMaterialShaderProgram(vertexLinker, fragmentLinker);
}
/**
* @private
*/
alternativa3d function getProceduresCRC32(targetObject:Object3D, opacityMap:TextureResource, alphaTest:int):uint {
var crc:uint = 0xFFFFFFFF;
var procedureCRC:uint;
var crc32Table:Vector.<uint> = Procedure.crc32Table;
if (targetObject.transformProcedure != null) {
procedureCRC = targetObject.transformProcedure.crc32;
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
}
if (targetObject.deltaTransformProcedure != null) {
procedureCRC = targetObject.deltaTransformProcedure.crc32;
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
if ((_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED) && _normalMap != null) {
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
}
}
if (_lightMap != null) {
procedureCRC = _passLightMapUVProcedure.crc32;
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
}
if (_normalMap != null) {
if (_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED) {
procedureCRC = (_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED) ? _passTBNRightProcedure.crc32 : _passTBNLeftProcedure.crc32;
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
procedureCRC = _getNormalTangentProcedure.crc32;
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
} else {
procedureCRC = _getNormalObjectProcedure.crc32;
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
}
}
procedureCRC = opacityMap != null ? getDiffuseOpacityProcedure.crc32 : getDiffuseProcedure.crc32;
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
if (alphaTest > 0) {
procedureCRC = alphaTest == 1 ? thresholdOpaqueAlphaProcedure.crc32 : thresholdTransparentAlphaProcedure.crc32;
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
}
if (_normalMap != null) {
procedureCRC = _applyReflectionNormalMapProcedure.crc32;
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
} else {
procedureCRC = _applyReflectionProcedure.crc32;
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
}
if (_lightMap != null) {
procedureCRC = _applyLightMapProcedure.crc32;
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
}
if (_reflectionMap != null) {
procedureCRC = _blendReflectionMap.crc32;
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
} else {
procedureCRC = _blendReflection.crc32;
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
}
if (fogMode == FogMode.SIMPLE) {
procedureCRC = passSimpleFogConstProcedure.crc32;
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
procedureCRC = outputWithSimpleFogProcedure.crc32;
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
} else if (fogMode == FogMode.ADVANCED) {
procedureCRC = postPassAdvancedFogConstProcedure.crc32;
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
procedureCRC = outputWithAdvancedFogProcedure.crc32;
crc = crc32Table[(crc ^ procedureCRC) & 0xFF] ^ (crc >> 8);
}
return crc ^ 0xFFFFFFFF;
}
private function getDrawUnit(program:EnvironmentMaterialShaderProgram, camera:Camera3D, surface:Surface, geometry:Geometry, opacityMap:TextureResource):DrawUnit {
// Buffers
var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
var uvBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[0]);
var normalsBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.NORMAL);
var tangentsBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TANGENT4);
if (positionBuffer == null || uvBuffer == null || normalsBuffer == null) return null;
var i:int;
var object:Object3D = surface.object;
if (program.sBump >= 0 && (_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED)) {
if (tangentsBuffer == null) return null;
}
// Draw call
var drawUnit:DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program);
drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cThresholdAlpha"), alphaThreshold, 0, 0, alpha);
// Set the textures
if (program.sLightMap >= 0) {
drawUnit.setTextureAt(program.sLightMap, _lightMap._texture);
drawUnit.setVertexBufferAt(program.aUV1, uvBuffer, geometry._attributesOffsets[VertexAttributes.TEXCOORDS[lightMapChannel]], VertexAttributes.FORMATS[VertexAttributes.TEXCOORDS[lightMapChannel]]);
}
if (program.sBump >= 0) {
drawUnit.setTextureAt(program.sBump, _normalMap._texture);
if (_normalMapSpace == NormalMapSpace.TANGENT_RIGHT_HANDED || _normalMapSpace == NormalMapSpace.TANGENT_LEFT_HANDED) {
drawUnit.setVertexBufferAt(program.aTangent, tangentsBuffer, geometry._attributesOffsets[VertexAttributes.TANGENT4], VertexAttributes.FORMATS[VertexAttributes.TANGENT4]);
}
}
if (program.sReflection >= 0) {
drawUnit.setTextureAt(program.sReflection, _reflectionMap._texture);
}
if (program.sOpacity >= 0) {
drawUnit.setTextureAt(program.sOpacity, opacityMap._texture);
}
// Set the streams
drawUnit.setVertexBufferAt(program.aPosition, positionBuffer, geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]);
drawUnit.setVertexBufferAt(program.aUV, uvBuffer, geometry._attributesOffsets[VertexAttributes.TEXCOORDS[0]], VertexAttributes.FORMATS[VertexAttributes.TEXCOORDS[0]]);
drawUnit.setVertexBufferAt(program.aNormal, normalsBuffer, geometry._attributesOffsets[VertexAttributes.NORMAL], VertexAttributes.FORMATS[VertexAttributes.NORMAL]);
// Set the constants
object.setTransformConstants(drawUnit, surface, program.vertexShader, camera);
drawUnit.setProjectionConstants(camera, program.cProjMatrix, object.localToCameraTransform);
drawUnit.setTextureAt(program.sTexture, diffuseMap._texture);
drawUnit.setTextureAt(program.sCubeMap, _environmentMap._texture);
var cameraToLocalTransform:Transform3D = object.cameraToLocalTransform;
drawUnit.setFragmentConstantsFromNumbers(program.cCamera, cameraToLocalTransform.d, cameraToLocalTransform.h, cameraToLocalTransform.l);
drawUnit.setFragmentConstantsFromNumbers(program.cAlpha, 0, 1 - reflection, reflection, alpha);
// Calculate local to global matrix
localToGlobalTransform.combine(camera.localToGlobalTransform, object.localToCameraTransform);
drawUnit.setFragmentConstantsFromTransform(program.cLocalToGlobal, localToGlobalTransform);
if (fogMode == FogMode.SIMPLE || fogMode == FogMode.ADVANCED) {
var lm:Transform3D = object.localToCameraTransform;
var dist:Number = fogFar - fogNear;
drawUnit.setVertexConstantsFromNumbers(program.vertexShader.getVariableIndex("cFogSpace"), lm.i/dist, lm.j/dist, lm.k/dist, (lm.l - fogNear)/dist);
drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cFogRange"), fogMaxDensity, 1, 0, 1 - fogMaxDensity);
}
if (fogMode == FogMode.SIMPLE) {
drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cFogColor"), fogColorR, fogColorG, fogColorB);
}
if (fogMode == FogMode.ADVANCED) {
if (fogTexture == null) {
var bmd:BitmapData = new BitmapData(32, 1, false, 0xFF0000);
for (i = 0; i < 32; i++) {
bmd.setPixel(i, 0, ((i/32)*255) << 16);
}
fogTexture = new BitmapTextureResource(bmd);
fogTexture.upload(camera.context3D);
}
var cLocal:Transform3D = camera.localToGlobalTransform;
var halfW:Number = camera.view.width/2;
var leftX:Number = -halfW*cLocal.a + camera.focalLength*cLocal.c;
var leftY:Number = -halfW*cLocal.e + camera.focalLength*cLocal.g;
var rightX:Number = halfW*cLocal.a + camera.focalLength*cLocal.c;
var rightY:Number = halfW*cLocal.e + camera.focalLength*cLocal.g;
// UV
var angle:Number = (Math.atan2(leftY, leftX) - Math.PI/2);
if (angle < 0) angle += Math.PI*2;
var dx:Number = rightX - leftX;
var dy:Number = rightY - leftY;
var lens:Number = Math.sqrt(dx*dx + dy*dy);
leftX /= lens;
leftY /= lens;
rightX /= lens;
rightY /= lens;
var uScale:Number = Math.acos(leftX*rightX + leftY*rightY)/Math.PI/2;
var uRight:Number = angle/Math.PI/2;
drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cFogConsts"), 0.5*uScale, 0.5 - uRight, 0);
drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sFogTexture"), fogTexture._texture);
}
return drawUnit;
}
private function getProgram(targetObject:Object3D, camera:Camera3D, opacityMap:TextureResource, alphaTest:int):EnvironmentMaterialShaderProgram {
// Renew program cache for this context
if (camera.context3D != cachedContext3D) {
cachedContext3D = camera.context3D;
programsCache = caches[cachedContext3D];
if (programsCache == null) {
programsCache = new Array();
caches[cachedContext3D] = programsCache;
}
}
var key:uint;
var program:EnvironmentMaterialShaderProgram;
key = getProceduresCRC32(targetObject, opacityMap, alphaTest);
program = programsCache[key];
if (program == null) {
program = programsCache[key] = setupProgram(targetObject, opacityMap, alphaTest);
program.upload(camera.context3D);
}
return program;
}
/**
* @private
*/
override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector.<Light3D>, lightsLength:int, objectRenderPriority:int = -1):void {
if (diffuseMap == null || diffuseMap._texture == null) return;
if (_environmentMap == null || _environmentMap._texture == null || !(_environmentMap._texture is CubeTexture)) return;
if (opacityMap != null && opacityMap._texture == null) return;
if (_normalMap != null && _normalMap._texture == null) return;
if (_reflectionMap != null && _reflectionMap._texture == null) return;
if (_lightMap != null && _lightMap._texture == null) return;
var object:Object3D = surface.object;
// Program
var program:EnvironmentMaterialShaderProgram;
var drawUnit:DrawUnit;
// Opaque pass
if (opaquePass && alphaThreshold <= alpha) {
if (alphaThreshold > 0) {
// Alpha test
// use opacityMap if it is presented
program = getProgram(object, camera, opacityMap, 1);
drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap);
} else {
// do not use opacityMap at all
program = getProgram(object, camera, null, 0);
drawUnit = getDrawUnit(program, camera, surface, geometry, null);
}
if (drawUnit == null) return;
// Use z-buffer within DrawCall, draws without blending
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE);
}
// Transparent pass
if (transparentPass && alphaThreshold > 0 && alpha > 0) {
// use opacityMap if it is presented
if (alphaThreshold <= alpha && !opaquePass) {
// Alpha threshold
program = getProgram(object, camera, opacityMap, 2);
drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap);
} else {
// There is no Alpha threshold or check z-buffer by previous pass
program = getProgram(object, camera, opacityMap, 0);
drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap);
}
if (drawUnit == null) return;
// Do not use z-buffer, draws with blending
drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA;
drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT);
}
}
//
/**
* @private
*/
alternativa3d function dirty():void {
for each (var program:EnvironmentMaterialShaderProgram in programsCache) {
program.dirty = true;
}
}
}
}
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.materials.ShaderProgram;
import alternativa.engine3d.materials.compiler.Linker;
use namespace alternativa3d;
class EnvironmentMaterialShaderProgram extends ShaderProgram {
public var aTangent:int = -1;
public var aNormal:int = -1;
public var aPosition:int = -1;
public var aUV:int = -1;
public var aUV1:int = -1;
public var cCamera:int = -1;
public var cLocalToGlobal:int = -1;
public var cAlpha:int = -1;
public var cProjMatrix:int = -1;
public var sBump:int = -1;
public var sTexture:int = -1;
public var sOpacity:int = -1;
public var sCubeMap:int = -1;
public var sReflection:int = -1;
public var sLightMap:int = -1;
public var dirty:Boolean = false;
public function EnvironmentMaterialShaderProgram(vertexShader:Linker, fragmentShader:Linker) {
super(vertexShader, fragmentShader);
fragmentShader.varyings = vertexShader.varyings;
vertexShader.link();
fragmentShader.link();
aPosition = vertexShader.findVariable("aPosition");
aNormal = vertexShader.findVariable("aNormal");
aUV = vertexShader.findVariable("aUV");
sBump = fragmentShader.findVariable("sBump");
aTangent = vertexShader.findVariable("aTangent");
sReflection = fragmentShader.findVariable("sReflection");
sLightMap = fragmentShader.findVariable("sLightMap");
aUV1 = vertexShader.findVariable("aUV1");
cProjMatrix = vertexShader.findVariable("cProjMatrix");
sTexture = fragmentShader.findVariable("sDiffuse");
sCubeMap = fragmentShader.findVariable("sCubeMap");
cCamera = fragmentShader.findVariable("cCamera");
cLocalToGlobal = fragmentShader.findVariable("cLocalToGlobal");
cAlpha = fragmentShader.findVariable("cAlpha");
sOpacity = fragmentShader.findVariable("sOpacity");
}
}

View File

@@ -0,0 +1,153 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.materials {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.DrawUnit;
import alternativa.engine3d.core.Light3D;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Renderer;
import alternativa.engine3d.core.VertexAttributes;
import alternativa.engine3d.materials.compiler.Linker;
import alternativa.engine3d.materials.compiler.Procedure;
import alternativa.engine3d.materials.compiler.VariableType;
import alternativa.engine3d.objects.Surface;
import alternativa.engine3d.resources.Geometry;
import flash.display3D.Context3D;
import flash.display3D.Context3DBlendFactor;
import flash.display3D.Context3DProgramType;
import flash.display3D.VertexBuffer3D;
import flash.utils.Dictionary;
use namespace alternativa3d;
/**
* The materiall fills surface with solid color in light-independent manner. Can draw a Skin with no more than 41 Joints per surface. See Skin.divide() for more details.
*
* @see alternativa.engine3d.objects.Skin#divide()
*/
public class FillMaterial extends Material {
private static var caches:Dictionary = new Dictionary(true);
private var cachedContext3D:Context3D;
private var programsCache:Dictionary;
private static var outColorProcedure:Procedure = new Procedure(["#c0=cColor", "mov o0, c0"], "outColorProcedure");
/**
* Transparency
*/
public var alpha:Number = 1;
private var red:Number;
private var green:Number;
private var blue:Number;
/**
* Color.
*/
public function get color():uint {
return (red*0xFF << 16) + (green*0xFF << 8) + blue*0xFF;
}
/**
* @private
*/
public function set color(value:uint):void {
red = ((value >> 16) & 0xFF)/0xFF;
green = ((value >> 8) & 0xFF)/0xFF;
blue = (value & 0xff)/0xFF;
}
/**
* Creates a new FillMaterial instance.
* @param color Color .
* @param alpha Transparency.
*/
public function FillMaterial(color:uint = 0x7F7F7F, alpha:Number = 1) {
this.color = color;
this.alpha = alpha;
}
private function setupProgram(object:Object3D):ShaderProgram {
var vertexLinker:Linker = new Linker(Context3DProgramType.VERTEX);
var positionVar:String = "aPosition";
vertexLinker.declareVariable(positionVar, VariableType.ATTRIBUTE);
if (object.transformProcedure != null) {
positionVar = appendPositionTransformProcedure(object.transformProcedure, vertexLinker);
}
vertexLinker.addProcedure(_projectProcedure);
vertexLinker.setInputParams(_projectProcedure, positionVar);
var fragmentLinker:Linker = new Linker(Context3DProgramType.FRAGMENT);
fragmentLinker.addProcedure(outColorProcedure);
fragmentLinker.varyings = vertexLinker.varyings;
return new ShaderProgram(vertexLinker, fragmentLinker);
}
/**
* @private
*/
override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector.<Light3D>, lightsLength:int, objectRenderPriority:int = -1):void {
var object:Object3D = surface.object;
// Strams
var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
// Check validity
if (positionBuffer == null) return;
// Program
// Renew program cache for this context
if (camera.context3D != cachedContext3D) {
cachedContext3D = camera.context3D;
programsCache = caches[cachedContext3D];
if (programsCache == null) {
programsCache = new Dictionary();
caches[cachedContext3D] = programsCache;
}
}
var program:ShaderProgram = programsCache[object.transformProcedure];
if (program == null) {
program = setupProgram(object);
program.upload(camera.context3D);
programsCache[object.transformProcedure] = program;
}
// Drawcall
var drawUnit:DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program);
// Streams
drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aPosition"), positionBuffer, geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]);
// Constants
object.setTransformConstants(drawUnit, surface, program.vertexShader, camera);
drawUnit.setProjectionConstants(camera, program.vertexShader.getVariableIndex("cProjMatrix"), object.localToCameraTransform);
drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cColor"), red, green, blue, alpha);
// Send to render
if (alpha < 1) {
drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA;
drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT);
} else {
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE);
}
}
/**
* @inheritDoc
*/
override public function clone():Material {
var res:FillMaterial = new FillMaterial(color, alpha);
res.clonePropertiesFrom(this);
return res;
}
}
}

View File

@@ -0,0 +1,24 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.materials {
/**
* @private
*/
public class FogMode {
public static const DISABLED:int = 0;
public static const SIMPLE:int = 1;
public static const ADVANCED:int = 2;
}
}

View File

@@ -0,0 +1,256 @@
/**
* Exhibit A - Source Code Form License Notice
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
package alternativa.engine3d.materials {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.DrawUnit;
import alternativa.engine3d.core.Light3D;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Renderer;
import alternativa.engine3d.core.VertexAttributes;
import alternativa.engine3d.materials.compiler.Linker;
import alternativa.engine3d.materials.compiler.Procedure;
import alternativa.engine3d.materials.compiler.VariableType;
import alternativa.engine3d.objects.Surface;
import alternativa.engine3d.resources.Geometry;
import alternativa.engine3d.resources.TextureResource;
import avmplus.getQualifiedClassName;
import flash.display3D.Context3D;
import flash.display3D.Context3DBlendFactor;
import flash.display3D.Context3DProgramType;
import flash.display3D.VertexBuffer3D;
import flash.utils.Dictionary;
import flash.utils.getDefinitionByName;
use namespace alternativa3d;
/**
* Texture material which supports light map. Can draw a Skin with no more than 41 Joints per surface. See Skin.divide() for more details.
* To be drawn with this material, geometry should have UV coordinates. Different UV-channels can be used for diffuse texture and light map.
*
* @see alternativa.engine3d.objects.Skin#divide()
* @see alternativa.engine3d.core.VertexAttributes#TEXCOORDS
*/
public class LightMapMaterial extends TextureMaterial {
private static var caches:Dictionary = new Dictionary(true);
private var cachedContext3D:Context3D;
private var programsCache:Dictionary;
// inputs: color
private static const _applyLightMapProcedure:Procedure = new Procedure([
"#v0=vUV1",
"#s0=sLightMap",
"tex t0, v0, s0 <2d,repeat,linear,miplinear>",
"add t0, t0, t0",
"mul i0.xyz, i0.xyz, t0.xyz",
"mov o0, i0"
], "applyLightMapProcedure");
private static const _passLightMapUVProcedure:Procedure = new Procedure([
"#a0=aUV1",
"#v0=vUV1",
"mov v0, a0"
], "passLightMapUVProcedure");
/**
* Light map.
*/
public var lightMap:TextureResource;
/**
* Number of the UV-channel for light map.
*/
public var lightMapChannel:uint = 0;
/**
* Creates a new LightMapMaterial instance.
* @param diffuseMap Diffuse texture.
* @param lightMap Light map.
* @param lightMapChannel Number of the UV-channel for light map.
*/
public function LightMapMaterial(diffuseMap:TextureResource = null, lightMap:TextureResource = null, lightMapChannel:uint = 0, opacityMap:TextureResource = null) {
super(diffuseMap, opacityMap);
this.lightMap = lightMap;
this.lightMapChannel = lightMapChannel;
}
/**
* @inheritDoc
*/
override public function clone():Material {
var res:LightMapMaterial = new LightMapMaterial(diffuseMap, lightMap, lightMapChannel, opacityMap);
res.clonePropertiesFrom(this);
return res;
}
/**
* @private
*/
override alternativa3d function fillResources(resources:Dictionary, resourceType:Class):void {
super.fillResources(resources, resourceType);
if (lightMap != null &&
A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(lightMap)) as Class, resourceType)) {
resources[lightMap] = true;
}
}
/**
* @param object
* @param programs
* @param camera
* @param opacityMap
* @param alphaTest 0 - disabled, 1 - opaque, 2 - contours
* @return
*/
private function getProgram(object:Object3D, programs:Vector.<ShaderProgram>, camera:Camera3D, opacityMap:TextureResource, alphaTest:int):ShaderProgram {
var key:int = (opacityMap != null ? 3 : 0) + alphaTest;
var program:ShaderProgram = programs[key];
if (program == null) {
// Make program
// Vertex shader
var vertexLinker:Linker = new Linker(Context3DProgramType.VERTEX);
var positionVar:String = "aPosition";
vertexLinker.declareVariable(positionVar, VariableType.ATTRIBUTE);
if (object.transformProcedure != null) {
positionVar = appendPositionTransformProcedure(object.transformProcedure, vertexLinker);
}
vertexLinker.addProcedure(_projectProcedure);
vertexLinker.setInputParams(_projectProcedure, positionVar);
vertexLinker.addProcedure(_passUVProcedure);
vertexLinker.addProcedure(_passLightMapUVProcedure);
// Pixel shader
var fragmentLinker:Linker = new Linker(Context3DProgramType.FRAGMENT);
fragmentLinker.declareVariable("tColor");
var outProcedure:Procedure = (opacityMap != null ? getDiffuseOpacityProcedure : getDiffuseProcedure);
fragmentLinker.addProcedure(outProcedure);
fragmentLinker.setOutputParams(outProcedure, "tColor");
if (alphaTest > 0) {
outProcedure = alphaTest == 1 ? thresholdOpaqueAlphaProcedure : thresholdTransparentAlphaProcedure;
fragmentLinker.addProcedure(outProcedure, "tColor");
fragmentLinker.setOutputParams(outProcedure, "tColor");
}
fragmentLinker.addProcedure(_applyLightMapProcedure, "tColor");
fragmentLinker.varyings = vertexLinker.varyings;
program = new ShaderProgram(vertexLinker, fragmentLinker);
program.upload(camera.context3D);
programs[key] = program;
}
return program;
}
private function getDrawUnit(program:ShaderProgram, camera:Camera3D, surface:Surface, geometry:Geometry, opacityMap:TextureResource):DrawUnit {
var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
var uvBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[0]);
var lightMapUVBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[lightMapChannel]);
var object:Object3D = surface.object;
// Drawcall
var drawUnit:DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program);
// Streams
drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aPosition"), positionBuffer, geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]);
drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aUV"), uvBuffer, geometry._attributesOffsets[VertexAttributes.TEXCOORDS[0]], VertexAttributes.FORMATS[VertexAttributes.TEXCOORDS[0]]);
drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aUV1"), lightMapUVBuffer, geometry._attributesOffsets[VertexAttributes.TEXCOORDS[lightMapChannel]], VertexAttributes.FORMATS[VertexAttributes.TEXCOORDS[lightMapChannel]]);
// Constants
object.setTransformConstants(drawUnit, surface, program.vertexShader, camera);
drawUnit.setProjectionConstants(camera, program.vertexShader.getVariableIndex("cProjMatrix"), object.localToCameraTransform);
drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cThresholdAlpha"), alphaThreshold, 0, 0, alpha);
// Textures
drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sDiffuse"), diffuseMap._texture);
drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sLightMap"), lightMap._texture);
if (opacityMap != null) {
drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sOpacity"), opacityMap._texture);
}
return drawUnit;
}
/**
* @private
*/
override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector.<Light3D>, lightsLength:int, objectRenderPriority:int = -1):void {
if (diffuseMap == null || lightMap == null || diffuseMap._texture == null || lightMap._texture == null) return;
if (opacityMap != null && opacityMap._texture == null) return;
var object:Object3D = surface.object;
// Buffers
var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
var uvBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[0]);
var lightMapUVBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[lightMapChannel]);
if (positionBuffer == null || uvBuffer == null || lightMapUVBuffer == null) return;
if (camera.context3D != cachedContext3D) {
cachedContext3D = camera.context3D;
programsCache = caches[cachedContext3D];
if (programsCache == null) {
programsCache = new Dictionary();
caches[cachedContext3D] = programsCache;
}
}
var optionsPrograms:Vector.<ShaderProgram> = programsCache[object.transformProcedure];
if(optionsPrograms == null) {
optionsPrograms = new Vector.<ShaderProgram>(6, true);
programsCache[object.transformProcedure] = optionsPrograms;
}
var program:ShaderProgram;
var drawUnit:DrawUnit;
// Opaque pass
if (opaquePass && alphaThreshold <= alpha) {
if (alphaThreshold > 0) {
// Alpha test
// use opacityMap if it is presented
program = getProgram(object, optionsPrograms, camera, opacityMap, 1);
drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap);
} else {
// do not use opacityMap at all
program = getProgram(object, optionsPrograms, camera, null, 0);
drawUnit = getDrawUnit(program, camera, surface, geometry, null);
}
// Use z-buffer within DrawCall, draws without blending
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE);
}
// Transparent pass
if (transparentPass && alphaThreshold > 0 && alpha > 0) {
// use opacityMap if it is presented
if (alphaThreshold <= alpha && !opaquePass) {
// Alpha threshold
program = getProgram(object, optionsPrograms, camera, opacityMap, 2);
drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap);
} else {
// There is no Alpha threshold or check z-buffer by previous pass
program = getProgram(object, optionsPrograms, camera, opacityMap, 0);
drawUnit = getDrawUnit(program, camera, surface, geometry, opacityMap);
}
// Do not use z-buffer, draws with blending
drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA;
drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More