a3d7 (2.7) moved to another folder

This commit is contained in:
Tubix
2024-10-05 15:14:39 +01:00
parent 1548517cf6
commit d57247f84d
580 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<actionScriptProperties mainApplicationPath="Engine3DLibrary.as" version="3">
<compiler additionalCompilerArguments="-compute-digest=false" copyDependentFiles="false" enableModuleDebug="false" flexSDK="Flex 4.0" generateAccessible="false" htmlExpressInstall="true" htmlGenerate="false" htmlHistoryManagement="false" htmlPlayerVersion="10.0.0" htmlPlayerVersionCheck="true" outputFolderPath="bin" sourceFolderPath="src" strict="true" useApolloConfig="false" verifyDigests="true" warn="true">
<compilerSourcePath/>
<libraryPath defaultLinkType="1">
<libraryPathEntry kind="4" path="">
<excludedEntries>
<libraryPathEntry kind="1" linkType="1" path="${PROJECT_FRAMEWORKS}/locale/{locale}"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/text_model.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/qtp.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/player/{targetPlayerMajorVersion}" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/framework.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/flex4.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/text_importExport.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/textLayout_conversion.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/text_edit.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="2" path="${PROJECT_FRAMEWORKS}/libs/utilities.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/textLayout_textField.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/textLayout_core.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/textLayout_edit.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/flex.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/rpc.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/datavisualization.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/framework_textLayout.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_dmv.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_flashflexkit.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_agent.swc" useDefaultLinkType="false"/>
</excludedEntries>
</libraryPathEntry>
</libraryPath>
<sourceAttachmentPath/>
</compiler>
<applications>
<application path="Engine3DLibrary.as"/>
</applications>
<modules/>
<buildCSSFiles/>
</actionScriptProperties>

View File

@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<flexLibProperties version="1">
<includeClasses>
<classEntry path="alternativa.engine3d.materials.Material"/>
<classEntry path="alternativa.Alternativa3D"/>
<classEntry path="alternativa.engine3d.objects.SkyBox"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeSource"/>
<classEntry path="alternativa.engine3d.core.Object3DContainer"/>
<classEntry path="alternativa.engine3d.objects.Skin"/>
<classEntry path="alternativa.engine3d.animation.TransformAnimation"/>
<classEntry path="alternativa.engine3d.controllers.SimpleObjectController"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeParam"/>
<classEntry path="alternativa.engine3d.animation.Track"/>
<classEntry path="alternativa.engine3d.core.Face"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeVisualScene"/>
<classEntry path="alternativa.engine3d.loaders.events.LoaderErrorEvent"/>
<classEntry path="alternativa.engine3d.core.Wrapper"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeLogger"/>
<classEntry path="alternativa.engine3d.materials.TextureMaterial"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeInput"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeVertices"/>
<classEntry path="alternativa.engine3d.core.Canvas"/>
<classEntry path="alternativa.engine3d.loaders.Parser3DS"/>
<classEntry path="alternativa.engine3d.objects.Sprite3D"/>
<classEntry path="alternativa.engine3d.loaders.MaterialLoader"/>
<classEntry path="alternativa.engine3d.core.Debug"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeEffect"/>
<classEntry path="alternativa.engine3d.core.Vertex"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeNode"/>
<classEntry path="alternativa.engine3d.primitives.Box"/>
<classEntry path="alternativa.engine3d.loaders.events.LoaderProgressEvent"/>
<classEntry path="alternativa.engine3d.objects.Joint"/>
<classEntry path="alternativa.engine3d.objects.VertexBinding"/>
<classEntry path="alternativa.engine3d.objects.Reference"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeAlternativa3DObject"/>
<classEntry path="alternativa.engine3d.loaders.collada.collada"/>
<classEntry path="alternativa.engine3d.core.Object3D"/>
<classEntry path="alternativa.engine3d.animation.MatrixKey"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeMaterial"/>
<classEntry path="alternativa.engine3d.objects.Axes"/>
<classEntry path="alternativa.engine3d.core.View"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeEffectParam"/>
<classEntry path="alternativa.engine3d.animation.Key"/>
<classEntry path="alternativa.engine3d.loaders.events.LoaderEvent"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaePrimitive"/>
<classEntry path="alternativa.engine3d.loaders.ParserCollada"/>
<classEntry path="alternativa.engine3d.animation.Animation"/>
<classEntry path="alternativa.engine3d.core.Geometry"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeImage"/>
<classEntry path="alternativa.engine3d.core.Sorting"/>
<classEntry path="alternativa.engine3d.core.Clipping"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeCamera"/>
<classEntry path="alternativa.engine3d.animation.ComplexAnimation"/>
<classEntry path="alternativa.engine3d.objects.Occluder"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeSampler"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeElement"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeAnimatedObject"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeGeometry"/>
<classEntry path="alternativa.engine3d.objects.LOD"/>
<classEntry path="alternativa.engine3d.core.KDNode"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeInstanceMaterial"/>
<classEntry path="alternativa.engine3d.containers.KDTree"/>
<classEntry path="alternativa.engine3d.core.MipMapping"/>
<classEntry path="alternativa.engine3d.containers.ConflictContainer"/>
<classEntry path="alternativa.engine3d.objects.Bone"/>
<classEntry path="alternativa.engine3d.objects.Mesh"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeInstanceController"/>
<classEntry path="alternativa.engine3d.animation.MatrixAnimation"/>
<classEntry path="alternativa.engine3d.primitives.GeoSphere"/>
<classEntry path="alternativa.engine3d.containers.ZSortContainer"/>
<classEntry path="alternativa.engine3d.animation.ObjectAnimation"/>
<classEntry path="alternativa.engine3d.core.MouseEvent3D"/>
<classEntry path="alternativa.engine3d.materials.FillMaterial"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeController"/>
<classEntry path="alternativa.engine3d.objects.AnimSprite"/>
<classEntry path="alternativa.engine3d.alternativa3d"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeArray"/>
<classEntry path="alternativa.engine3d.animation.PointKey"/>
<classEntry path="alternativa.engine3d.core.Camera3D"/>
<classEntry path="alternativa.engine3d.animation.ValueKey"/>
<classEntry path="alternativa.engine3d.primitives.Plane"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeDocument"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeChannel"/>
</includeClasses>
<includeResources/>
<namespaceManifests/>
</flexLibProperties>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>Alternativa3D</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.adobe.flexbuilder.project.flexbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.maven.ide.eclipse.maven2Nature</nature>
<nature>com.adobe.flexbuilder.project.flexlibnature</nature>
<nature>com.adobe.flexbuilder.project.actionscriptnature</nature>
</natures>
<linkedResources>
</linkedResources>
</projectDescription>

View File

@@ -0,0 +1,83 @@
#Fri Feb 12 14:04:16 YEKT 2010
BlankLines.BeforeFirstMember=0
BlankLines.BeforePackage=0
BlankLines.BeforeTypeInPackage=1
BlankLines.BeforeTypeOutsidePackage=1
BlankLines.BetweenFields=0
BlankLines.BetweenFunctions=1
BlankLines.BetweenMembergrous=1
BlankLines.EatBlankLines=false
Braces.Catch=false
Braces.DoWhile=false
Braces.Else=false
Braces.Finally=false
Braces.For=false
Braces.Function=false
Braces.Getter=false
Braces.If=false
Braces.Method=false
Braces.ObjectInitialiser=false
Braces.Package=false
Braces.Setter=false
Braces.Switch=false
Braces.Try=false
Braces.Type=false
Braces.While=false
Braces.With=false
Indentation.Case=true
Indentation.Catch=true
Indentation.DoWhile=true
Indentation.Else=true
Indentation.Finally=true
Indentation.For=true
Indentation.Functions=true
Indentation.Getters=true
Indentation.If=true
Indentation.Methods=true
Indentation.Package=true
Indentation.Setters=true
Indentation.SpacesPerTab=4
Indentation.Switch=true
Indentation.Try=true
Indentation.Types=true
Indentation.UseSpaces=false
Indentation.UseTabs=true
Indentation.While=true
WhiteSpace.BlankAfterAdditiveOperator=true
WhiteSpace.BlankAfterArgumentComma=true
WhiteSpace.BlankAfterArgumentList=false
WhiteSpace.BlankAfterArrayInitialiser=false
WhiteSpace.BlankAfterAssign=true
WhiteSpace.BlankAfterBinaryOperator=true
WhiteSpace.BlankAfterCommaInArrayInit=true
WhiteSpace.BlankAfterCommaInObjectInit=true
WhiteSpace.BlankAfterConditionalColon=true
WhiteSpace.BlankAfterConditionalHook=true
WhiteSpace.BlankAfterConditionalOperator=true
WhiteSpace.BlankAfterEquality=true
WhiteSpace.BlankAfterFunctionName=false
WhiteSpace.BlankAfterMultiplicativeOperator=true
WhiteSpace.BlankAfterPrefixOperator=false
WhiteSpace.BlankAfterRelationalOperator=true
WhiteSpace.BlankAfterShiftOperator=true
WhiteSpace.BlankAfterVaraiableTypeColon=true
WhiteSpace.BlankAfterVariableInitialiser=true
WhiteSpace.BlankBeforeAdditiveOperator=true
WhiteSpace.BlankBeforeArgumentComma=false
WhiteSpace.BlankBeforeArgumentList=false
WhiteSpace.BlankBeforeArrayInitialiser=false
WhiteSpace.BlankBeforeAssign=true
WhiteSpace.BlankBeforeBinaryOperator=true
WhiteSpace.BlankBeforeCommaInArrayInit=false
WhiteSpace.BlankBeforeCommaInObjectInit=false
WhiteSpace.BlankBeforeConditionalColon=true
WhiteSpace.BlankBeforeConditionalHook=true
WhiteSpace.BlankBeforeConditionalOperator=true
WhiteSpace.BlankBeforeEquality=true
WhiteSpace.BlankBeforeMultiplicativeOperator=true
WhiteSpace.BlankBeforePostfixOperator=false
WhiteSpace.BlankBeforeRelationalOperator=true
WhiteSpace.BlankBeforeShiftOperator=true
WhiteSpace.BlankBeforeVaraiableTypeColon=true
WhiteSpace.BlankBeforeVariableInitialiser=true
eclipse.preferences.version=1

View File

@@ -0,0 +1,7 @@
#Tue Feb 16 17:42:22 YEKT 2010
com.powerflasher.fdt.core.DefaultOutputFolder=bin
com.powerflasher.fdt.core.Language=AS3
com.powerflasher.fdt.core.LanguageType=Flex_3_SDK_0_for_FP_10
com.powerflasher.fdt.core.useProjectFormatterSettings=false
com.powerflasher.fdt.core.useProjectProblems=false
eclipse.preferences.version=1

View File

@@ -0,0 +1,3 @@
#Thu Feb 14 09:12:30 YEKT 2008
eclipse.preferences.version=1
encoding/<project>=UTF-8

View File

@@ -0,0 +1,3 @@
#Tue Nov 13 17:53:36 YEKT 2007
eclipse.preferences.version=1
org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false

View File

@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="flex" name="Flex">
<configuration flex_sdk="flex_sdk_4 (player 10)">
<option name="DO_BUILD" value="true" />
<option name="OUTPUT_TYPE" value="Library" />
<option name="USE_DEFAULT_SDK_CONFIG_FILE" value="true" />
<option name="USE_CUSTOM_CONFIG_FILE" value="false" />
<option name="CUSTOM_CONFIG_FILE" value="" />
<option name="USE_CUSTOM_CONFIG_FILE_FOR_TESTS" value="false" />
<option name="CUSTOM_CONFIG_FILE_FOR_TESTS" value="" />
<option name="APPLICATION_ENTRY_POINT" value="" />
<option name="MAIN_CLASS" value="" />
<option name="OUTPUT_FILE_NAME" value="Alternativa3D-2.7.3.0-SNAPSHOT.swc" />
<option name="USE_FACET_COMPILE_OUTPUT_PATH" value="true" />
<option name="FACET_COMPILE_OUTPUT_PATH" value="$MODULE_DIR$/target" />
<option name="INCLUDE_RESOURCE_FILES_IN_SWC" value="false" />
<option name="TARGET_PLAYER_VERSION" value="10.0.0" />
<option name="STATIC_LINK_RUNTIME_SHARED_LIBRARIES" value="false" />
<option name="USE_LOCALE_SETTINGS" value="false" />
<option name="LOCALE" value="en_US" />
<option name="ADDITIONAL_COMPILER_OPTIONS" value="" />
<option name="VERSION" value="3" />
<option name="PATH_TO_SERVICES_CONFIG_XML" value="" />
<option name="CONTEXT_ROOT" value="" />
<NAMESPACE_AND_MANIFEST_FILE_INFO_LIST />
<CONDITIONAL_COMPILATION_DEFINITION_LIST />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/target/generated-sources/platform" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target/classes" />
<excludeFolder url="file://$MODULE_DIR$/target/surefire-reports" />
<excludeFolder url="file://$MODULE_DIR$/target/test-classes" />
</content>
<orderEntry type="jdk" jdkName="flex_sdk_4 (player 10)" jdkType="Flex SDK Type" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library">
<library name="AUTOGENERATED library equal to Flex SDK flex_sdk_4 (player 10)">
<CLASSES>
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/player/10/playerglobal.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/textLayout_core.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/textLayout_edit.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/textLayout_conversion.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/framework_textLayout.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/flex4.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/flex.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/textLayout_textField.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/rpc.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/utilities.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/framework.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/locale/en_US/rpc_rb.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/locale/en_US/flex4_rb.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/locale/en_US/framework_rb.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/locale/en_US/airframework_rb.swc!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/air/ServiceMonitor/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/air/ApplicationUpdater/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/utilities/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/airframework/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/wireframe/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/flex4/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/framework_textLayout/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/haloclassic/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/framework/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/rpc/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/flex/src" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="library" name="Maven: platform.clients.fp10.libraries:AlternativaOSGi:swc:2.0.3.0" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:playerglobal:swc:10-4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:textLayout_conversion:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:textLayout_core:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:flex4:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:framework:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:applicationupdater:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:applicationupdater_ui:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:textLayout_edit:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:flex:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:utilities:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:rpc:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:rpc:rb.swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:framework:rb.swc:4.0.0.4021" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.maven.wagon:wagon-webdav:1.0-beta-2" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: slide:slide-webdavlib:2.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: commons-httpclient:commons-httpclient:2.0.2" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: commons-logging:commons-logging:1.0.4" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: jdom:jdom:1.0" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: de.zeigermann.xml:xml-im-exporter:1.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.maven.wagon:wagon-provider-api:1.0-beta-2" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.codehaus.plexus:plexus-utils:1.0.4" level="project" />
</component>
</module>

View File

@@ -0,0 +1 @@
Bundle-Name: platform.clients.fp10.libraries.Alternativa3D

View File

@@ -0,0 +1,33 @@
<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>2.7.3.0</version>
<parent>
<groupId>platform.clients.fp10.tools.maven</groupId>
<artifactId>BasePom</artifactId>
<version>2.0.6.0</version>
</parent>
<scm>
<connection>scm:svn:http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/Alternativa3D/tags/2.7.3.0</connection>
</scm>
<dependencies>
<dependency>
<groupId>platform.clients.fp10.libraries</groupId>
<artifactId>AlternativaOSGi</artifactId>
<type>swc</type>
<scope>external</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>platform.clients.fp10.libraries</groupId>
<artifactId>AlternativaOSGi</artifactId>
<type>swc</type>
<version>2.0.3.0</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@@ -0,0 +1,14 @@
package alternativa {
/**
* Класс содержит информацию о версии библиотеки.
* Также используется для интеграции библиотеки в среду разработки Adobe Flash.
*/
public class Alternativa3D {
/**
* Версия библиотеки в формате: поколение.feature-версия.fix-версия
*/
public static const version:String = "7.0.0";
}
}

View File

@@ -0,0 +1,3 @@
package alternativa.engine3d {
public namespace alternativa3d = "http://alternativaplatform.com/en/alternativa3d";
}

View File

@@ -0,0 +1,67 @@
package alternativa.engine3d.animation {
import flash.utils.getTimer;
public class Animation {
public var speed:Number = 1;
protected var _position:Number = 0;
private var _playing:Boolean = false;
private var time:int;
/**
* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
*/
public function play():void {
time = getTimer();
_playing = true;
}
/**
* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
*/
public function stop():void {
_playing = false;
}
/**
* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
*/
public function update():void {
if (_playing) {
var t:int = getTimer();
position += (t - time)*0.001*speed;
time = t;
control();
}
}
public function get position():Number {
return _position;
}
public function set position(value:Number):void {
_position = value;
control();
}
protected function control():void {
}
/**
* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
* @return <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD>
*/
public function get playing():Boolean {
return _playing;
}
/**
* Возвращает длину анимации
*/
public function get length():Number {
return 0;
}
}
}

View File

@@ -0,0 +1,62 @@
package alternativa.engine3d.animation {
public class ComplexAnimation extends Animation {
private var _numAnimations:int;
private var animations:Vector.<Animation>;
public function ComplexAnimation() {
animations = new Vector.<Animation>();
}
public function addAnimation(animation:Animation):Animation {
if (animation == null) {
throw new Error("Animation cannot be null");
}
animations[_numAnimations++] = animation;
return animation;
}
public function removeAnimation(animation:Animation):Animation {
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;
return animation;
}
public function get numAnimations():int {
return _numAnimations;
}
public function getAnimationAt(index:int):Animation {
return animations[index];
}
override protected function control():void {
for (var i:int = 0; i < _numAnimations; i++) {
var animation:Animation = animations[i];
if (animation.playing) {
animation.position = _position;
}
}
}
override public function get length():Number {
var maxLen:Number = 0;
for (var i:int = 0; i < _numAnimations; i++) {
var time:Number = animations[i].length;
if (time > maxLen) {
maxLen = time;
}
}
return maxLen;
}
}
}

View File

@@ -0,0 +1,21 @@
package alternativa.engine3d.animation {
import alternativa.engine3d.alternativa3d;
use namespace alternativa3d;
public class Key {
// <20><><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
public var time:Number;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
public var next:Key;
public function Key(time:Number) {
this.time = time;
}
alternativa3d function interpolate(time:Number, next:Key, key:Key = null):Key {
return key;
}
}
}

View File

@@ -0,0 +1,20 @@
package alternativa.engine3d.animation {
public class MatrixAnimation extends ObjectAnimation {
public var matrix:Track;
private var matrixKey:MatrixKey = new MatrixKey(0, null);
override protected function control():void {
if (matrix != null) {
matrix.getKey(_position, matrixKey);
object.setMatrix(matrixKey.matrix);
}
}
override public function get length():Number {
return (matrix != null) ? matrix.length : 0;
}
}
}

View File

@@ -0,0 +1,44 @@
package alternativa.engine3d.animation {
import alternativa.engine3d.alternativa3d;
import flash.geom.Matrix3D;
use namespace alternativa3d;
public class MatrixKey extends Key {
private static const tempMatrix:Matrix3D = new Matrix3D();
public var matrix:Matrix3D;
public function MatrixKey(time:Number, matrix:Matrix3D) {
super(time);
this.matrix = matrix;
}
override alternativa3d function interpolate(time:Number, next:Key, key:Key = null):Key {
var mat:Matrix3D;
if (next != null) {
mat = tempMatrix;
mat.identity();
mat.append(matrix);
mat.interpolateTo((next as MatrixKey).matrix, (time - this.time)/(next.time - this.time));
} else {
mat = matrix;
}
if (key != null) {
key.time = time;
MatrixKey(key).matrix = mat;
return key;
} else {
return new MatrixKey(time, mat);
}
}
public function toString():String {
return "[MatrixKey " + time + ":" + matrix.rawData + "]";
}
}
}

View File

@@ -0,0 +1,9 @@
package alternativa.engine3d.animation {
import alternativa.engine3d.core.Object3D;
public class ObjectAnimation extends Animation {
public var object:Object3D;
}
}

View File

@@ -0,0 +1,48 @@
package alternativa.engine3d.animation {
import alternativa.engine3d.alternativa3d;
use namespace alternativa3d;
public class PointKey extends Key {
public var x:Number;
public var y:Number;
public var z:Number;
public function PointKey(time:Number, x:Number, y:Number, z:Number) {
super(time);
this.x = x;
this.y = y;
this.z = z;
}
override alternativa3d function interpolate(time:Number, next:Key, key:Key = null):Key {
var x:Number;
var y:Number;
var z:Number;
if (next != null) {
var t:Number = (time - this.time)/(next.time - this.time);
x = this.x + (PointKey(next).x - this.x)*t;
y = this.y + (PointKey(next).y - this.y)*t;
z = this.z + (PointKey(next).z - this.z)*t;
} else {
x = this.x;
y = this.y;
z = this.z;
}
if (key != null) {
key.time = time;
PointKey(key).x = x;
PointKey(key).y = y;
PointKey(key).z = z;
return key;
} else {
return new PointKey(time, x, y, z);
}
}
public function toString():String {
return "[PointKey " + time.toFixed(3) + ":" + x + "," + y + "," + z + "]";
}
}
}

View File

@@ -0,0 +1,104 @@
package alternativa.engine3d.animation {
import alternativa.engine3d.alternativa3d;
use namespace alternativa3d;
public class Track {
public var keyList:Key;
public function addKey(key:Key):void {
key.next = keyList;
keyList = key;
}
public function sortKeys():void {
keyList = sortKeysByTime(keyList);
}
public function getKey(time:Number, key:Key = null):Key {
var prev:Key;
var next:Key = keyList;
while (next != null && next.time < time) {
prev = next;
next = next.next;
}
if (prev != null) return prev.interpolate(time, next, key);
if (next != null) return next.interpolate(time, null, key);
return null;
}
private function sortKeysByTime(list:Key):Key {
var left:Key = list;
var right:Key = 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 = sortKeysByTime(left);
}
if (right.next != null) {
right = sortKeysByTime(right);
}
var flag:Boolean = left.time < right.time;
if (flag) {
list = left;
left = left.next;
} else {
list = right;
right = right.next;
}
var last:Key = list;
while (true) {
if (left == null) {
last.next = right;
return list;
} else if (right == null) {
last.next = left;
return list;
}
if (flag) {
if (left.time < right.time) {
last = left;
left = left.next;
} else {
last.next = right;
last = right;
right = right.next;
flag = false;
}
} else {
if (right.time < left.time) {
last = right;
right = right.next;
} else {
last.next = left;
last = left;
left = left.next;
flag = true;
}
}
}
return null;
}
/**
* Возвращает время последнего ключа
*/
public function get length():Number {
if (keyList != null) {
var key:Key = keyList;
while (key.next != null) {
key = key.next;
}
return key.time;
} else {
return 0;
}
}
}
}

View File

@@ -0,0 +1,137 @@
package alternativa.engine3d.animation {
public class TransformAnimation extends ObjectAnimation {
public var translation:Track;
public var rotation:Track;
public var scale:Track;
public var x:Track;
public var y:Track;
public var z:Track;
public var rotationX:Track;
public var rotationY:Track;
public var rotationZ:Track;
public var scaleX:Track;
public var scaleY:Track;
public var scaleZ:Track;
private var valueKey:ValueKey = new ValueKey(0, 0);
private var pointKey:PointKey = new PointKey(0, 0, 0, 0);
override protected function control():void {
if (translation != null) {
translation.getKey(_position, pointKey);
object.x = pointKey.x;
object.y = pointKey.y;
object.z = pointKey.z;
} else {
if (x != null) {
x.getKey(_position, valueKey);
object.x = valueKey.value;
}
if (y != null) {
y.getKey(_position, valueKey);
object.y = valueKey.value;
}
if (z != null) {
z.getKey(_position, valueKey);
object.z = valueKey.value;
}
}
if (rotation != null) {
rotation.getKey(_position, pointKey);
object.rotationX = pointKey.x;
object.rotationY = pointKey.y;
object.rotationZ = pointKey.z;
} else {
if (rotationX != null) {
rotationX.getKey(_position, valueKey);
object.rotationX = valueKey.value;
}
if (rotationY != null) {
rotationY.getKey(_position, valueKey);
object.rotationY = valueKey.value;
}
if (rotationZ != null) {
rotationZ.getKey(_position, valueKey);
object.rotationZ = valueKey.value;
}
}
if (scale != null) {
scale.getKey(_position, pointKey);
object.scaleX = pointKey.x;
object.scaleY = pointKey.y;
object.scaleZ = pointKey.z;
} else {
if (scaleX != null) {
scaleX.getKey(_position, valueKey);
object.scaleX = valueKey.value;
}
if (scaleY != null) {
scaleY.getKey(_position, valueKey);
object.scaleY = valueKey.value;
}
if (scaleZ != null) {
scaleZ.getKey(_position, valueKey);
object.scaleZ = valueKey.value;
}
}
}
override public function get length():Number {
var len:Number;
var maxLen:Number = 0;
if (translation != null) {
maxLen = translation.length;
}
if (rotation != null) {
len = rotation.length;
maxLen = (len > maxLen) ? len : maxLen;
}
if (scale != null) {
len = scale.length;
maxLen = (len > maxLen) ? len : maxLen;
}
if (x != null) {
len = x.length;
maxLen = (len > maxLen) ? len : maxLen;
}
if (y != null) {
len = y.length;
maxLen = (len > maxLen) ? len : maxLen;
}
if (z != null) {
len = z.length;
maxLen = (len > maxLen) ? len : maxLen;
}
if (rotationX != null) {
len = rotationX.length;
maxLen = (len > maxLen) ? len : maxLen;
}
if (rotationY != null) {
len = rotationY.length;
maxLen = (len > maxLen) ? len : maxLen;
}
if (rotationZ != null) {
len = rotationZ.length;
maxLen = (len > maxLen) ? len : maxLen;
}
if (scaleX != null) {
len = scaleX.length;
maxLen = (len > maxLen) ? len : maxLen;
}
if (scaleY != null) {
len = scaleY.length;
maxLen = (len > maxLen) ? len : maxLen;
}
if (scaleZ != null) {
len = scaleZ.length;
maxLen = (len > maxLen) ? len : maxLen;
}
return maxLen;
}
}
}

View File

@@ -0,0 +1,34 @@
package alternativa.engine3d.animation {
import alternativa.engine3d.alternativa3d;
use namespace alternativa3d;
public class ValueKey extends Key {
public var value:Number;
public function ValueKey(time:Number, value:Number) {
super(time);
this.value = value;
}
override alternativa3d function interpolate(time:Number, next:Key, key:Key = null):Key {
var value:Number;
if (next != null) {
value = this.value + (ValueKey(next).value - this.value)*(time - this.time)/(next.time - this.time);
} else {
value = this.value;
}
if (key != null) {
key.time = time;
ValueKey(key).value = value;
return key;
} else {
return new ValueKey(time, value);
}
}
public function toString():String {
return "[ValueKey " + time + ":" + value + "]";
}
}
}

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,64 @@
package alternativa.engine3d.containers {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Canvas;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Object3DContainer;
use namespace alternativa3d;
/**
* Контейнер, дочерние объекты которого отрисовываются по удалённости от камеры
*/
public class ZSortContainer extends Object3DContainer {
static private const sortingStack:Vector.<int> = new Vector.<int>();
override protected function drawVisibleChildren(camera:Camera3D, object:Object3D, canvas:Canvas):void {
var i:int;
var j:int;
var l:int = 0;
var r:int = numVisibleChildren - 1;
var child:Object3D;
var stackIndex:int;
var left:Number;
var median:Number;
var right:Number;
sortingStack[0] = l;
sortingStack[1] = r;
stackIndex = 2;
while (stackIndex > 0) {
r = sortingStack[--stackIndex];
l = sortingStack[--stackIndex];
j = r;
i = l;
child = visibleChildren[(r + l) >> 1];
median = child.ml;
do {
while ((left = (visibleChildren[i] as Object3D).ml) > median) i++;
while ((right = (visibleChildren[j] as Object3D).ml) < median) j--;
if (i <= j) {
child = visibleChildren[i];
visibleChildren[i++] = visibleChildren[j];
visibleChildren[j--] = child;
}
} while (i <= j);
if (l < j) {
sortingStack[stackIndex++] = l;
sortingStack[stackIndex++] = j;
}
if (i < r) {
sortingStack[stackIndex++] = i;
sortingStack[stackIndex++] = r;
}
}
// Отрисовка
for (i = numVisibleChildren - 1; i >= 0; i--) {
child = visibleChildren[i];
child.draw(camera, child, canvas);
visibleChildren[i] = null;
}
}
}
}

View File

@@ -0,0 +1,461 @@
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;
/**
*
*/
public class SimpleObjectController {
/**
* Имя действия для привязки клавиш движения вперёд.
*/
public static const ACTION_FORWARD:String = "ACTION_FORWARD";
/**
* Имя действия для привязки клавиш движения назад.
*/
public static const ACTION_BACK:String = "ACTION_BACK";
/**
* Имя действия для привязки клавиш движения влево.
*/
public static const ACTION_LEFT:String = "ACTION_LEFT";
/**
* Имя действия для привязки клавиш движения вправо.
*/
public static const ACTION_RIGHT:String = "ACTION_RIGHT";
/**
* Имя действия для привязки клавиш движения вверх.
*/
public static const ACTION_UP:String = "ACTION_UP";
/**
* Имя действия для привязки клавиш движения вниз.
*/
public static const ACTION_DOWN:String = "ACTION_DOWN";
/**
* Имя действия для привязки клавиш поворота вверх.
*/
public static const ACTION_PITCH_UP:String = "ACTION_PITCH_UP";
/**
* Имя действия для привязки клавиш поворота вниз.
*/
public static const ACTION_PITCH_DOWN:String = "ACTION_PITCH_DOWN";
/**
* Имя действия для привязки клавиш поворота налево.
*/
public static const ACTION_YAW_LEFT:String = "ACTION_YAW_LEFT";
/**
* Имя действия для привязки клавиш поворота направо.
*/
public static const ACTION_YAW_RIGHT:String = "ACTION_YAW_RIGHT";
/**
* Имя действия для привязки клавиш увеличения скорости.
*/
public static const ACTION_ACCELERATE:String = "ACTION_ACCELERATE";
/**
* Имя действия для привязки клавиш активации обзора мышью.
*/
public static const ACTION_MOUSE_LOOK:String = "ACTION_MOUSE_LOOK";
public var speed:Number;
public var speedMultiplier:Number;
public var mouseSensitivity:Number;
public var maxPitch:Number = Number.MAX_VALUE;
public var minPitch:Number = -Number.MAX_VALUE;
private var eventSource:InteractiveObject;
private var _object:Object3D;
private var _up:Boolean;
private var _down:Boolean;
private var _forward:Boolean;
private var _back:Boolean;
private var _left:Boolean;
private var _right:Boolean;
private var _accelerate:Boolean;
private var displacement:Vector3D = new Vector3D();
private var mousePoint:Point = new Point();
private var mouseLook:Boolean;
private var objectTransform:Vector.<Vector3D>;
private var time:int;
/**
* Ассоциативный массив, связывающий имена команд с реализующими их функциями. Функции должны иметь вид
* function(value:Boolean):void. Значение параметра <code>value</code> указывает, нажата или отпущена соответствующая команде
* клавиша.
*/
private var actionBindings:Object = {};
/**
* Ассоциативный массив, связывающий коды клавиатурных клавиш с именами команд.
*/
protected var keyBindings:Object = {};
/**
*
* @param eventSource источник событий для контроллера
* @param speed скорость поступательного перемещения объекта
* @param mouseSensitivity чувствительность мыши - количество градусов поворота на один пиксель перемещения мыши
*/
public function SimpleObjectController(eventSource:InteractiveObject, object:Object3D, speed:Number, speedMultiplier:Number = 3, mouseSensitivity:Number = 1) {
this.eventSource = eventSource;
this.object = object;
this.speed = speed;
this.speedMultiplier = speedMultiplier;
this.mouseSensitivity = mouseSensitivity;
actionBindings[ACTION_FORWARD] = moveForward;
actionBindings[ACTION_BACK] = moveBack;
actionBindings[ACTION_LEFT] = moveLeft;
actionBindings[ACTION_RIGHT] = moveRight;
actionBindings[ACTION_UP] = moveUp;
actionBindings[ACTION_DOWN] = moveDown;
actionBindings[ACTION_ACCELERATE] = accelerate;
setDefaultBindings();
enable();
}
/**
* Активирует контроллер.
*/
public function enable():void {
eventSource.addEventListener(KeyboardEvent.KEY_DOWN, onKey);
eventSource.addEventListener(KeyboardEvent.KEY_UP, onKey);
eventSource.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
eventSource.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
/**
* Деактивирует контроллер.
*/
public function disable():void {
eventSource.removeEventListener(KeyboardEvent.KEY_DOWN, onKey);
eventSource.removeEventListener(KeyboardEvent.KEY_UP, onKey);
eventSource.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
eventSource.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
stopMouseLook();
}
/**
*
*/
private function onMouseDown(e:MouseEvent):void {
startMouseLook();
}
/**
*
*/
private function onMouseUp(e:MouseEvent):void {
stopMouseLook();
}
/**
* Включает режим взгляда мышью.
*/
public function startMouseLook():void {
mousePoint.x = eventSource.mouseX;
mousePoint.y = eventSource.mouseY;
mouseLook = true;
}
/**
* Отключает режим взгляда мышью.
*/
public function stopMouseLook():void {
mouseLook = false;
}
/**
*
*/
private function onKey(e:KeyboardEvent):void {
var method:Function = keyBindings[e.keyCode];
if (method != null) method.call(this, e.type == KeyboardEvent.KEY_DOWN);
}
/**
* Управляемый объект.
*/
public function set object(value:Object3D):void {
_object = value;
updateObjectTransform();
}
/**
*
*/
public function get object():Object3D {
return _object;
}
/**
* Обновляет инофрмацию о трансформации объекта. Метод следует вызывать после изменения матрицы объекта вне контролллера.
*/
public function updateObjectTransform():void {
if (_object != null) objectTransform = _object.getMatrix().decompose();
}
/**
* Вычисляет новое положение объекта, используя внутренний счётчик времени.
*/
public function update():void {
if (_object == null) return;
var frameTime:Number = time;
time = getTimer();
frameTime = 0.001*(time - frameTime);
if (frameTime > 0.1) frameTime = 0.1;
var moved:Boolean = false;
if (mouseLook) {
var dx:Number = eventSource.mouseX - mousePoint.x;
var dy:Number = eventSource.mouseY - mousePoint.y;
mousePoint.x = eventSource.mouseX;
mousePoint.y = eventSource.mouseY;
var v:Vector3D = objectTransform[1];
v.x -= dy*Math.PI/180*mouseSensitivity;
if (v.x > maxPitch) v.x = maxPitch;
if (v.x < minPitch) v.x = minPitch;
v.z -= dx*Math.PI/180*mouseSensitivity;
moved = true;
}
displacement.x = _right ? 1 : (_left ? -1 : 0);
displacement.y = _forward ? 1 : (_back ? -1 : 0);
displacement.z = _up ? 1 : (_down ? -1 : 0);
if (displacement.lengthSquared > 0) {
if (_object is Camera3D) {
var tmp:Number = displacement.z;
displacement.z = displacement.y;
displacement.y = -tmp;
}
deltaTransformVector(displacement);
if (_accelerate) displacement.scaleBy(speedMultiplier*speed*frameTime/displacement.length);
else displacement.scaleBy(speed*frameTime/displacement.length);
(objectTransform[0] as Vector3D).incrementBy(displacement);
moved = true;
}
if (moved) {
var m:Matrix3D = new Matrix3D();
m.recompose(objectTransform);
_object.setMatrix(m);
}
}
/**
*
* @param pos
*/
public function setObjectPos(pos:Vector3D):void {
if (_object != null) {
var v:Vector3D = objectTransform[0];
v.x = pos.x;
v.y = pos.y;
v.z = pos.z;
}
}
/**
*
* @param x
* @param y
* @param 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;
}
}
/**
*
* @param point
*/
public function lookAt(point:Vector3D):void {
lookAtXYZ(point.x, point.y, point.z);
}
/**
*
* @param x
* @param y
* @param z
*/
public function lookAtXYZ(x:Number, y:Number, z:Number):void {
if (_object == null) return;
var v:Vector3D = objectTransform[0];
var dx:Number = x - v.x;
var dy:Number = y - v.y;
var dz:Number = z - v.z;
v = objectTransform[1];
v.x = Math.atan2(dz, Math.sqrt(dx*dx + dy*dy));
if (_object is Camera3D) v.x -= 0.5*Math.PI;
v.y = 0;
v.z = -Math.atan2(dx, dy);
var m:Matrix3D = _object.getMatrix();
m.recompose(objectTransform);
_object.setMatrix(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.getMatrix().transformVectors(_vin, _vout);
var c:Vector3D = objectTransform[0];
v.x = _vout[0] - c.x;
v.y = _vout[1] - c.y;
v.z = _vout[2] - c.z;
}
/**
* Активация движения вперёд.
*
* @param value <code>true</code> для начала движения, <code>false</code> для окончания
*/
public function moveForward(value:Boolean):void {
_forward = value;
}
/**
* Активация движения назад.
*
* @param value <code>true</code> для начала движения, <code>false</code> для окончания
*/
public function moveBack(value:Boolean):void {
_back = value;
}
/**
* Активация движения влево.
*
* @param value <code>true</code> для начала движения, <code>false</code> для окончания
*/
public function moveLeft(value:Boolean):void {
_left = value;
}
/**
* Активация движения вправо.
*
* @param value <code>true</code> для начала движения, <code>false</code> для окончания
*/
public function moveRight(value:Boolean):void {
_right = value;
}
/**
* Активация движения вверх.
*
* @param value <code>true</code> для начала движения, <code>false</code> для окончания
*/
public function moveUp(value:Boolean):void {
_up = value;
}
/**
* Активация движения вниз.
*
* @param value <code>true</code> для начала движения, <code>false</code> для окончания
*/
public function moveDown(value:Boolean):void {
_down = value;
}
/**
* Активация режима увеличенной скорости.
*
* @param value <code>true</code> для включения ускорения, <code>false</code> для выключения
*/
public function accelerate(value:Boolean):void {
_accelerate = value;
}
/**
* Метод выполняет привязку клавиши к действию. Одной клавише может быть назначено только одно действие.
*
* @param keyCode код клавиши
* @param action наименование действия
*
* @see #unbindKey()
* @see #unbindAll()
*/
public function bindKey(keyCode:uint, action:String):void {
var method:Function = actionBindings[action];
if (method != null) keyBindings[keyCode] = method;
}
/**
*
*/
public function bindKeys(bindings:Array):void {
for (var i:int = 0; i < bindings.length; i += 2) bindKey(bindings[i], bindings[i + 1]);
}
/**
* Очистка привязки клавиши.
*
* @param keyCode код клавиши
*
* @see #bindKey()
* @see #unbindAll()
*/
public function unbindKey(keyCode:uint):void {
delete keyBindings[keyCode];
}
/**
* Очистка привязки всех клавиш.
*
* @see #bindKey()
* @see #unbindKey()
*/
public function unbindAll():void {
for (var key:String in keyBindings) delete keyBindings[key];
}
/**
* Метод устанавливает привязки клавиш по умолчанию. Реализация по умолчанию не делает ничего.
*
* @see #bindKey()
* @see #unbindKey()
* @see #unbindAll()
*/
public function setDefaultBindings():void {
bindKey(87, ACTION_FORWARD);
bindKey(83, ACTION_BACK);
bindKey(65, ACTION_LEFT);
bindKey(68, ACTION_RIGHT);
bindKey(69, ACTION_UP);
bindKey(67, ACTION_DOWN);
bindKey(Keyboard.SHIFT, ACTION_ACCELERATE);
bindKey(Keyboard.UP, ACTION_FORWARD);
bindKey(Keyboard.DOWN, ACTION_BACK);
bindKey(Keyboard.LEFT, ACTION_LEFT);
bindKey(Keyboard.RIGHT, ACTION_RIGHT);
}
}
}

View File

@@ -0,0 +1,608 @@
package alternativa.engine3d.core {
import alternativa.engine3d.alternativa3d;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.events.Event;
import flash.geom.Matrix3D;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.geom.Vector3D;
import flash.system.System;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.utils.Dictionary;
import flash.utils.getDefinitionByName;
import flash.utils.getQualifiedClassName;
import flash.utils.getQualifiedSuperclassName;
import flash.utils.getTimer;
use namespace alternativa3d;
public class Camera3D extends Object3D {
/**
* Вьюпорт
*/
public var view:View;
/**
* Поле зрения в радианах.
*/
public var fov:Number = Math.PI/2;
public var nearClipping:Number = 0;
public var farClipping:Number = 5000;
// Параметры перспективы
alternativa3d var viewSizeX:Number;
alternativa3d var viewSizeY:Number;
alternativa3d var focalLength:Number;
// Перекрытия
alternativa3d var occluders:Vector.<Vertex> = new Vector.<Vertex>();
alternativa3d var numOccluders:int;
alternativa3d var occludedAll:Boolean;
alternativa3d var numDraws:int;
alternativa3d var numPolygons:int;
alternativa3d var numTriangles:int;
/**
*
* @param fov
* @param nearClipping
* @param farClipping
*/
public function Camera3D(fov:Number = 1.5707963267948966, nearClipping:Number = 0, farClipping:Number = 5000) {
this.fov = fov;
this.nearClipping = nearClipping;
this.farClipping = farClipping;
}
/**
* Отрисовка иерархии объектов, в которой находится камера.
* Перед render(), если менялись параметры камеры, нужно вызвать updateProjection().
*/
public function render():void {
if (view != null) {
// Расчёт параметров проецирования
viewSizeX = view._width*0.5;
viewSizeY = view._height*0.5;
focalLength = Math.sqrt(viewSizeX*viewSizeX + viewSizeY*viewSizeY)/Math.tan(fov*0.5);
// Расчёт матрицы перевода из глобального пространства в камеру
composeMatrix();
var root:Object3D = this;
while (root._parent != null) {
root = root._parent;
root.composeMatrix();
appendMatrix(root);
}
invertMatrix();
// Сброс окклюдеров
numOccluders = 0;
occludedAll = false;
// Сброс счётчиков
numDraws = 0;
numPolygons = 0;
numTriangles = 0;
// Сброс отрисовок
view.numDraws = 0;
// Отрисовка
if (root != this && root.visible) {
root.appendMatrix(this);
if (root.cullingInCamera(this, root, 63) >= 0) {
root.draw(this, root, view);
// Отложенное удаление вершин и граней в коллектор
deferredDestroy();
// Зачистка окклюдеров
clearOccluders();
}
}
// Зачистка ненужных канвасов
view.removeChildren(view.numDraws);
// Обработка интерактивности после рендера
if (view._interactive) {
view.onMouseMove();
}
}
}
override alternativa3d function composeMatrix():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*viewSizeX/focalLength;
var sinXscaleY:Number = sinX*scaleY*viewSizeY/focalLength;
var cosXscaleY:Number = cosX*scaleY*viewSizeY/focalLength;
var cosXscaleZ:Number = cosX*scaleZ;
var sinXscaleZ:Number = sinX*scaleZ;
ma = cosZ*cosYscaleX;
mb = cosZsinY*sinXscaleY - sinZ*cosXscaleY;
mc = cosZsinY*cosXscaleZ + sinZ*sinXscaleZ;
md = x;
me = sinZ*cosYscaleX;
mf = sinZsinY*sinXscaleY + cosZ*cosXscaleY;
mg = sinZsinY*cosXscaleZ - cosZ*sinXscaleZ;
mh = y;
mi = -sinY*scaleX;
mj = cosY*sinXscaleY;
mk = cosY*cosXscaleZ;
ml = z;
}
private function invertMatrix():void {
var a:Number = ma;
var b:Number = mb;
var c:Number = mc;
var d:Number = md;
var e:Number = me;
var f:Number = mf;
var g:Number = mg;
var h:Number = mh;
var i:Number = mi;
var j:Number = mj;
var k:Number = mk;
var l:Number = ml;
var det:Number = 1/(-c*f*i + b*g*i + c*e*j - a*g*j - b*e*k + a*f*k);
ma = (-g*j + f*k)*det;
mb = (c*j - b*k)*det;
mc = (-c*f + b*g)*det;
md = (d*g*j - c*h*j - d*f*k + b*h*k + c*f*l - b*g*l)*det;
me = (g*i - e*k)*det;
mf = (-c*i + a*k)*det;
mg = (c*e - a*g)*det;
mh = (c*h*i - d*g*i + d*e*k - a*h*k - c*e*l + a*g*l)*det;
mi = (-f*i + e*j)*det;
mj = (b*i - a*j)*det;
mk = (-b*e + a*f)*det;
ml = (d*f*i - b*h*i - d*e*j + a*h*j + b*e*l - a*f*l)*det;
}
/**
* @param v
* @param result
*/
public function projectGlobal(v:Vector3D, result:Vector3D):void {
composeMatrix();
var root:Object3D = this;
while (root._parent != null) {
root = root._parent;
root.composeMatrix();
appendMatrix(root);
}
invertMatrix();
var x:Number = ma*v.x + mb*v.y + mc*v.z + md;
var y:Number = me*v.x + mf*v.y + mg*v.z + mh;
var z:Number = mi*v.x + mj*v.y + mk*v.z + ml;
result.x = x*viewSizeX/z + view._width/2;
result.y = y*viewSizeY/z + view._height/2;
result.z = z;
}
// DEBUG
// Режим отладки
public var debug:Boolean = false;
// Список объектов дебага
private var debugSet:Object = new Object();
// Добавить в дебаг
public function addToDebug(debug:int, ... rest):void {
if (!debugSet[debug]) debugSet[debug] = new Dictionary();
for (var i:int = 0; i < rest.length;) debugSet[debug][rest[i++]] = true;
}
// Убрать из дебага
public function removeFromDebug(debug:int, ... rest):void {
if (debugSet[debug]) {
for (var i:int = 0; i < rest.length;) delete debugSet[debug][rest[i++]];
var key:*;
for (key in debugSet[debug]) break;
if (!key) delete debugSet[debug];
}
}
// Проверка, находится ли объект или один из классов, от которых он нследован, в дебаге
alternativa3d function checkInDebug(object:Object3D):int {
var res:int = 0;
for (var debug:int = 1; debug <= 512; debug <<= 1) {
if (debugSet[debug]) {
if (debugSet[debug][Object3D] || debugSet[debug][object]) {
res |= debug;
} else {
var objectClass:Class = getDefinitionByName(getQualifiedClassName(object)) as Class;
while (objectClass != Object3D) {
if (debugSet[debug][objectClass]) {
res |= debug;
break;
}
objectClass = Class(getDefinitionByName(getQualifiedSuperclassName(objectClass)));
}
}
}
}
return res;
}
public var diagram:Sprite = createDiagram();
private var fpsTextField:TextField;
private var memoryTextField:TextField;
private var drawsTextField:TextField;
private var polygonsTextField:TextField;
private var trianglesTextField:TextField;
private var timerTextField:TextField;
private var graph:Bitmap;
private var rect:Rectangle;
private var _diagramAlign:String = "TR";
private var _diagramHorizontalMargin:Number = 2;
private var _diagramVerticalMargin:Number = 2;
public var fpsUpdatePeriod:int = 10;
private var fpsUpdateCounter:int;
private var previousFrameTime:int;
private var previousPeriodTime:int;
private var maxMemory:int;
public var timerUpdatePeriod:int = 10;
private var timerUpdateCounter:int;
private var timeSum:int;
private var timeCount:int;
private var timer:int;
private function createDiagram():Sprite {
var diagram:Sprite = new Sprite();
diagram.mouseEnabled = false;
diagram.mouseChildren = false;
// Инициализация диаграммы
diagram.addEventListener(Event.ADDED_TO_STAGE, function():void {
// FPS
fpsTextField = new TextField();
fpsTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0xCCCCCC);
fpsTextField.autoSize = TextFieldAutoSize.LEFT;
fpsTextField.text = "FPS:";
fpsTextField.selectable = false;
fpsTextField.x = -3;
fpsTextField.y = -5;
diagram.addChild(fpsTextField);
fpsTextField = new TextField();
fpsTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0xCCCCCC);
fpsTextField.autoSize = TextFieldAutoSize.RIGHT;
fpsTextField.text = Number(diagram.stage.frameRate).toFixed(2);
fpsTextField.selectable = false;
fpsTextField.x = -3;
fpsTextField.y = -5;
fpsTextField.width = 65;
diagram.addChild(fpsTextField);
// Время выполнения метода
timerTextField = new TextField();
timerTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0x0066FF);
timerTextField.autoSize = TextFieldAutoSize.LEFT;
timerTextField.text = "MS:";
timerTextField.selectable = false;
timerTextField.x = -3;
timerTextField.y = 4;
diagram.addChild(timerTextField);
timerTextField = new TextField();
timerTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0x0066FF);
timerTextField.autoSize = TextFieldAutoSize.RIGHT;
timerTextField.text = "";
timerTextField.selectable = false;
timerTextField.x = -3;
timerTextField.y = 4;
timerTextField.width = 65;
diagram.addChild(timerTextField);
// Память
memoryTextField = new TextField();
memoryTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0xCCCC00);
memoryTextField.autoSize = TextFieldAutoSize.LEFT;
memoryTextField.text = "MEM:";
memoryTextField.selectable = false;
memoryTextField.x = -3;
memoryTextField.y = 13;
diagram.addChild(memoryTextField);
memoryTextField = new TextField();
memoryTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0xCCCC00);
memoryTextField.autoSize = TextFieldAutoSize.RIGHT;
memoryTextField.text = bytesToString(System.totalMemory);
memoryTextField.selectable = false;
memoryTextField.x = -3;
memoryTextField.y = 13;
memoryTextField.width = 65;
diagram.addChild(memoryTextField);
// Отрисовочные вызовы
drawsTextField = new TextField();
drawsTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0x00CC00);
drawsTextField.autoSize = TextFieldAutoSize.LEFT;
drawsTextField.text = "DRW:";
drawsTextField.selectable = false;
drawsTextField.x = -3;
drawsTextField.y = 22;
diagram.addChild(drawsTextField);
drawsTextField = new TextField();
drawsTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0x00CC00);
drawsTextField.autoSize = TextFieldAutoSize.RIGHT;
drawsTextField.text = "0";
drawsTextField.selectable = false;
drawsTextField.x = -3;
drawsTextField.y = 22;
drawsTextField.width = 52;
diagram.addChild(drawsTextField);
// Полигоны
polygonsTextField = new TextField();
polygonsTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0xFF0033);
polygonsTextField.autoSize = TextFieldAutoSize.LEFT;
polygonsTextField.text = "PLG:";
polygonsTextField.selectable = false;
polygonsTextField.x = -3;
polygonsTextField.y = 31;
diagram.addChild(polygonsTextField);
polygonsTextField = new TextField();
polygonsTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0xFF0033);
polygonsTextField.autoSize = TextFieldAutoSize.RIGHT;
polygonsTextField.text = "0";
polygonsTextField.selectable = false;
polygonsTextField.x = -3;
polygonsTextField.y = 31;
polygonsTextField.width = 52;
diagram.addChild(polygonsTextField);
// Треугольники
trianglesTextField = new TextField();
trianglesTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0xFF6600);
trianglesTextField.autoSize = TextFieldAutoSize.LEFT;
trianglesTextField.text = "TRI:";
trianglesTextField.selectable = false;
trianglesTextField.x = -3;
trianglesTextField.y = 40;
diagram.addChild(trianglesTextField);
trianglesTextField = new TextField();
trianglesTextField.defaultTextFormat = new TextFormat("Tahoma", 10, 0xFF6600);
trianglesTextField.autoSize = TextFieldAutoSize.RIGHT;
trianglesTextField.text = "0";
trianglesTextField.selectable = false;
trianglesTextField.x = -3;
trianglesTextField.y = 40;
trianglesTextField.width = 52;
diagram.addChild(trianglesTextField);
// График
graph = new Bitmap(new BitmapData(60, 40, true, 0x20FFFFFF));
rect = new Rectangle(0, 0, 1, 40);
graph.x = 0;
graph.y = 54;
diagram.addChild(graph);
// Сброс параметров
previousPeriodTime = getTimer();
previousFrameTime = previousPeriodTime;
fpsUpdateCounter = 0;
maxMemory = 0;
timerUpdateCounter = 0;
timeSum = 0;
timeCount = 0;
// Подписка
diagram.stage.addEventListener(Event.ENTER_FRAME, updateDiagram, false, -1000);
diagram.stage.addEventListener(Event.RESIZE, resizeDiagram, false, -1000);
resizeDiagram();
});
// Деинициализация диаграммы
diagram.addEventListener(Event.REMOVED_FROM_STAGE, function():void {
// Обнуление
diagram.removeChild(fpsTextField);
diagram.removeChild(memoryTextField);
diagram.removeChild(drawsTextField);
diagram.removeChild(polygonsTextField);
diagram.removeChild(trianglesTextField);
diagram.removeChild(timerTextField);
diagram.removeChild(graph);
fpsTextField = null;
memoryTextField = null;
drawsTextField = null;
polygonsTextField = null;
trianglesTextField = null;
timerTextField = null;
graph.bitmapData.dispose();
graph = null;
rect = null;
// Отписка
diagram.stage.removeEventListener(Event.ENTER_FRAME, updateDiagram);
diagram.stage.removeEventListener(Event.RESIZE, resizeDiagram);
});
return diagram;
}
private function resizeDiagram(e:Event = null):void {
if (diagram.stage != null) {
var coord:Point = diagram.parent.globalToLocal(new Point());
if (_diagramAlign == StageAlign.TOP_LEFT || _diagramAlign == StageAlign.LEFT || _diagramAlign == StageAlign.BOTTOM_LEFT) {
diagram.x = Math.round(coord.x + _diagramHorizontalMargin);
}
if (_diagramAlign == StageAlign.TOP || _diagramAlign == StageAlign.BOTTOM) {
diagram.x = Math.round(coord.x + diagram.stage.stageWidth/2 - graph.width/2);
}
if (_diagramAlign == StageAlign.TOP_RIGHT || _diagramAlign == StageAlign.RIGHT || _diagramAlign == StageAlign.BOTTOM_RIGHT) {
diagram.x = Math.round(coord.x + diagram.stage.stageWidth - _diagramHorizontalMargin - graph.width);
}
if (_diagramAlign == StageAlign.TOP_LEFT || _diagramAlign == StageAlign.TOP || _diagramAlign == StageAlign.TOP_RIGHT) {
diagram.y = Math.round(coord.y + _diagramVerticalMargin);
}
if (_diagramAlign == StageAlign.LEFT || _diagramAlign == StageAlign.RIGHT) {
diagram.y = Math.round(coord.y + diagram.stage.stageHeight/2 - (graph.y + graph.height)/2);
}
if (_diagramAlign == StageAlign.BOTTOM_LEFT || _diagramAlign == StageAlign.BOTTOM || _diagramAlign == StageAlign.BOTTOM_RIGHT) {
diagram.y = Math.round(coord.y + diagram.stage.stageHeight - _diagramVerticalMargin - graph.y - graph.height);
}
}
}
private function updateDiagram(e:Event):void {
var value:Number;
var mod:int;
var time:int = getTimer();
var stageFrameRate:int = diagram.stage.frameRate;
// FPS текст
if (++fpsUpdateCounter == fpsUpdatePeriod) {
value = 1000*fpsUpdatePeriod/(time - previousPeriodTime);
if (value > stageFrameRate) value = stageFrameRate;
mod = value*100 % 100;
fpsTextField.text = int(value) + "." + ((mod >= 10) ? mod : ((mod > 0) ? ("0" + mod) : "00"));
previousPeriodTime = time;
fpsUpdateCounter = 0;
}
// FPS график
value = 1000/(time - previousFrameTime);
if (value > stageFrameRate) value = stageFrameRate;
graph.bitmapData.scroll(1, 0);
graph.bitmapData.fillRect(rect, 0x20FFFFFF);
graph.bitmapData.setPixel32(0, 40*(1 - value/stageFrameRate), 0xFFCCCCCC);
previousFrameTime = time;
// Время текст
if (++timerUpdateCounter == timerUpdatePeriod) {
if (timeCount > 0) {
value = timeSum/timeCount;
mod = value*100 % 100;
timerTextField.text = int(value) + "." + ((mod >= 10) ? mod : ((mod > 0) ? ("0" + mod) : "00"));
} else {
timerTextField.text = "";
}
timerUpdateCounter = 0;
timeSum = 0;
timeCount = 0;
}
// Память текст
var memory:int = System.totalMemory;
value = memory/1048576;
mod = value*100 % 100;
memoryTextField.text = int(value) + "." + ((mod >= 10) ? mod : ((mod > 0) ? ("0" + mod) : "00"));
// Память график
if (memory > maxMemory) maxMemory = memory;
graph.bitmapData.setPixel32(0, 40*(1 - memory/maxMemory), 0xFFCCCC00);
// Отрисовочные вызовы текст
drawsTextField.text = String(numDraws);
// Полигоны текст
polygonsTextField.text = String(numPolygons);
// Треугольники текст
trianglesTextField.text = String(numTriangles);
}
public function startTimer():void {
timer = getTimer();
}
public function stopTimer():void {
timeSum += getTimer() - timer;
timeCount++;
}
public function get diagramAlign():String {
return _diagramAlign;
}
public function set diagramAlign(value:String):void {
_diagramAlign = value;
resizeDiagram();
}
public function get diagramHorizontalMargin():Number {
return _diagramHorizontalMargin;
}
public function set diagramHorizontalMargin(value:Number):void {
_diagramHorizontalMargin = value;
resizeDiagram();
}
public function get diagramVerticalMargin():Number {
return _diagramVerticalMargin;
}
public function set diagramVerticalMargin(value:Number):void {
_diagramVerticalMargin = value;
resizeDiagram();
}
private function bytesToString(bytes:int):String {
if (bytes < 1024) return bytes + "b";
else if (bytes < 10240) return (bytes/1024).toFixed(2) + "kb";
else if (bytes < 102400) return (bytes/1024).toFixed(1) + "kb";
else if (bytes < 1048576) return (bytes >> 10) + "kb";
else if (bytes < 10485760) return (bytes/1048576).toFixed(2);// + "mb";
else if (bytes < 104857600) return (bytes/1048576).toFixed(1);// + "mb";
else return String(bytes >> 20);// + "mb";
}
// Отложенное удаление в коллектор
private var firstVertex:Vertex = new Vertex();
private var firstFace:Face = new Face();
private var firstWrapper:Wrapper = new Wrapper();
alternativa3d var lastWrapper:Wrapper = firstWrapper;
alternativa3d var lastVertex:Vertex = firstVertex;
alternativa3d var lastFace:Face = firstFace;
private function deferredDestroy():void {
for (var face:Face = firstFace.next; face != null; face = face.next) {
var w:Wrapper = face.wrapper;
if (w != null) {
for (var lw:Wrapper = null; w != null; lw = w,w = w.next) {
w.vertex = null;
}
lastWrapper.next = face.wrapper;
lastWrapper = lw;
}
face.material = null;
face.wrapper = null;
//face.processNext = null;
//face.geometry = null;
}
if (firstFace != lastFace) {
lastFace.next = Face.collector;
Face.collector = firstFace.next;
firstFace.next = null;
lastFace = firstFace;
}
if (firstWrapper != lastWrapper) {
lastWrapper.next = Wrapper.collector;
Wrapper.collector = firstWrapper.next;
firstWrapper.next = null;
lastWrapper = firstWrapper;
}
if (firstVertex != lastVertex) {
lastVertex.next = Vertex.collector;
Vertex.collector = firstVertex.next;
firstVertex.next = null;
lastVertex = firstVertex;
}
}
alternativa3d function clearOccluders():void {
for (var i:int = 0; i < numOccluders; i++) {
var first:Vertex = occluders[i];
//for (var last:Vertex = first; last.next != null; last = last.next);
var last:Vertex = first;
while (last.next != null) last = last.next;
last.next = Vertex.collector;
Vertex.collector = first;
occluders[i] = null;
}
numOccluders = 0;
}
}
}

View File

@@ -0,0 +1,108 @@
package alternativa.engine3d.core {
import alternativa.engine3d.alternativa3d;
import flash.display.DisplayObject;
import flash.display.Graphics;
import flash.display.Sprite;
import flash.geom.ColorTransform;
use namespace alternativa3d;
public class Canvas extends Sprite {
static private const defaultColorTransform:ColorTransform = new ColorTransform();
static private const collector:Vector.<Canvas> = new Vector.<Canvas>();
static private var collectorLength:int = 0;
alternativa3d var gfx:Graphics = graphics;
private var modifiedGraphics:Boolean;
private var modifiedAlpha:Boolean;
private var modifiedBlendMode:Boolean;
private var modifiedColorTransform:Boolean;
private var modifiedFilters:Boolean;
alternativa3d var _numChildren:int = 0;
alternativa3d var numDraws:int = 0;
alternativa3d var interactiveObject:Object3D;
/*public function Canvas() {
mouseEnabled = false;
mouseChildren = false;
}*/
alternativa3d function getChildCanvas(interactiveObject:Object3D, useGraphics:Boolean, useChildren:Boolean, alpha:Number = 1, blendMode:String = "normal", colorTransform:ColorTransform = null, filters:Array = null):Canvas {
var canvas:Canvas;
var displayObject:DisplayObject;
// Зачистка не канвасов
while (_numChildren > numDraws && !((displayObject = getChildAt(_numChildren - 1 - numDraws)) is Canvas)) {
removeChild(displayObject);
_numChildren--;
}
// Получение канваса
if (_numChildren > numDraws++) {
canvas = displayObject as Canvas;
// Зачистка
if (canvas.modifiedGraphics) {
canvas.gfx.clear();
}
if (canvas._numChildren > 0 && !useChildren) {
canvas.removeChildren(0);
}
} else {
canvas = (collectorLength > 0) ? collector[--collectorLength] : new Canvas();
addChildAt(canvas, 0);
_numChildren++;
}
// Сохранение интерактивного объекта
canvas.interactiveObject = interactiveObject;
// Пометка о том, что в graphics будет что-то нарисовано
canvas.modifiedGraphics = useGraphics;
// Установка свойств
if (alpha != 1) {
canvas.alpha = alpha;
canvas.modifiedAlpha = true;
} else if (canvas.modifiedAlpha) {
canvas.alpha = 1;
canvas.modifiedAlpha = false;
}
if (blendMode != "normal") {
canvas.blendMode = blendMode;
canvas.modifiedBlendMode = true;
} else if (canvas.modifiedBlendMode) {
canvas.blendMode = "normal";
canvas.modifiedBlendMode = false;
}
if (colorTransform != null) {
colorTransform.alphaMultiplier = alpha;
canvas.transform.colorTransform = colorTransform;
canvas.modifiedColorTransform = true;
} else if (canvas.modifiedColorTransform) {
defaultColorTransform.alphaMultiplier = alpha;
canvas.transform.colorTransform = defaultColorTransform;
canvas.modifiedColorTransform = false;
}
if (filters != null) {
canvas.filters = filters;
canvas.modifiedFilters = true;
} else if (canvas.modifiedFilters) {
canvas.filters = null;
canvas.modifiedFilters = false;
}
return canvas;
}
alternativa3d function removeChildren(keep:int):void {
for (var canvas:Canvas; _numChildren > keep; _numChildren--) {
if ((canvas = removeChildAt(0) as Canvas) != null) {
canvas.interactiveObject = null;
if (canvas.modifiedGraphics) canvas.gfx.clear();
if (canvas._numChildren > 0) canvas.removeChildren(0);
collector[collectorLength++] = canvas;
}
}
}
}
}

View File

@@ -0,0 +1,19 @@
package alternativa.engine3d.core {
public class Clipping {
/**
* Объект отсекается целиком, если он полностью вне пирамиды видимости или пересекает nearClipping камеры.
*/
static public const BOUND_CULLING:int = 0;
/**
* Грань отсекается целиком, если она полностью вне пирамиды видимости или пересекает nearClipping камеры.
*/
static public const FACE_CULLING:int = 1;
/**
* Грань подрезается пирамидой видимости камеры.
*/
static public const FACE_CLIPPING:int = 2;
}
}

View File

@@ -0,0 +1,190 @@
package alternativa.engine3d.core {
import alternativa.engine3d.alternativa3d;
use namespace alternativa3d;
public class Debug {
static public const NAMES:int = 1;
static public const AXES:int = 2;
static public const CENTERS:int = 4;
static public const BOUNDS:int = 8;
static public const EDGES:int = 16;
static public const VERTICES:int = 32;
static public const NORMALS:int = 64;
static public const NODES:int = 128;
static public const SPLITS:int = 256;
static public const BONES:int = 512;
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);
static alternativa3d function drawBounds(camera:Camera3D, canvas:Canvas, object:Object3D, boundMinX:Number, boundMinY:Number, boundMinZ:Number, boundMaxX:Number, boundMaxY:Number, boundMaxZ:Number, color:int = -1):void {
var vertex:Vertex;
// Заполнение
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;
// Трансформация в камеру
for (vertex = a; vertex != null; vertex = vertex.next) {
vertex.cameraX = object.ma*vertex.x + object.mb*vertex.y + object.mc*vertex.z + object.md;
vertex.cameraY = object.me*vertex.x + object.mf*vertex.y + object.mg*vertex.z + object.mh;
vertex.cameraZ = object.mi*vertex.x + object.mj*vertex.y + object.mk*vertex.z + object.ml;
if (vertex.cameraZ <= 0) return;
}
// Проецирование
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;
}
// Отрисовка
canvas.gfx.lineStyle(0, (color < 0) ? ((object.culling > 0) ? 0xFFFF00 : 0x00FF00) : color);
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);
static alternativa3d function drawKDNode(camera:Camera3D, canvas:Canvas, object:Object3D, axis:int, coord:Number, boundMinX:Number, boundMinY:Number, boundMinZ:Number, boundMaxX:Number, boundMaxY:Number, boundMaxZ:Number, alpha:Number):void {
var vertex:Vertex;
// Заполнение
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;
}
// Трансформация в камеру
for (vertex = a; vertex != null; vertex = vertex.next) {
vertex.cameraX = object.ma*vertex.x + object.mb*vertex.y + object.mc*vertex.z + object.md;
vertex.cameraY = object.me*vertex.x + object.mf*vertex.y + object.mg*vertex.z + object.mh;
vertex.cameraZ = object.mi*vertex.x + object.mj*vertex.y + object.mk*vertex.z + object.ml;
if (vertex.cameraZ <= 0) return;
}
// Проецирование
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;
}
// Отрисовка
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);
}
}
}

View File

@@ -0,0 +1,179 @@
package alternativa.engine3d.core {
import alternativa.engine3d.materials.Material;
public class Face {
public var material:Material;
public var next:Face;
public var negative:Face;
public var positive:Face;
public var wrapper:Wrapper;
public var normalX:Number;
public var normalY:Number;
public var normalZ:Number;
public var offset:Number;
public var processNext:Face;
public var distance:Number;
public var geometry:Geometry;
static public var collector:Face;
static public function create():Face {
if (collector != null) {
var res:Face = collector;
collector = res.next;
res.next = null;
/*if (res.processNext != null) trace("!!!processNext!!!");
if (res.geometry != null) trace("!!!geometry!!!");
if (res.negative != null) trace("!!!negative!!!");
if (res.positive != null) trace("!!!positive!!!");*/
return res;
} else {
//trace("new Face");
return new Face();
}
}
public function create():Face {
if (collector != null) {
var res:Face = collector;
collector = res.next;
res.next = null;
/*if (res.processNext != null) trace("!!!processNext!!!");
if (res.geometry != null) trace("!!!geometry!!!");
if (res.negative != null) trace("!!!negative!!!");
if (res.positive != null) trace("!!!positive!!!");*/
return res;
} else {
//trace("new Face");
return new Face();
}
}
/**
* Расчёт нормали
* @param normalize Флаг нормализации
*/
/*public function calculateNormal(normalize:Boolean = false):void {
var w:Wrapper = wrapper;
var a:Vertex = w.vertex; w = w.next;
var b:Vertex = w.vertex; w = w.next;
var c:Vertex = w.vertex;
var abx:Number = b.x - a.x;
var aby:Number = b.y - a.y;
var abz:Number = b.z - a.z;
var acx:Number = c.x - a.x;
var acy:Number = c.y - a.y;
var acz:Number = c.z - a.z;
normalX = acz*aby - acy*abz;
normalY = acx*abz - acz*abx;
normalZ = acy*abx - acx*aby;
if (normalize) {
var length:Number = normalX*normalX + normalY*normalY + normalZ*normalZ;
if (length > 0.001) {
length = 1/Math.sqrt(length);
normalX *= length;
normalY *= length;
normalZ *= length;
}
}
offset = a.x*normalX + a.y*normalY + a.z*normalZ;
}*/
/**
* Выстраивание вершин в лучшую последовательность и расчёт нормали
* @return Если грань не вырождена - true, иначе false
*/
public function calculateBestSequenceAndNormal():void {
if (wrapper.next.next.next != null) {
var max:Number = -1e+22;
var s:Wrapper;
var sm:Wrapper;
var sp:Wrapper;
for (w = wrapper; w != null; w = w.next) {
var wn:Wrapper = (w.next != null) ? w.next : wrapper;
var wm:Wrapper = (wn.next != null) ? wn.next : wrapper;
a = w.vertex;
b = wn.vertex;
c = wm.vertex;
abx = b.x - a.x;
aby = b.y - a.y;
abz = b.z - a.z;
acx = c.x - a.x;
acy = c.y - a.y;
acz = c.z - a.z;
nx = acz*aby - acy*abz;
ny = acx*abz - acz*abx;
nz = acy*abx - acx*aby;
nl = nx*nx + ny*ny + nz*nz;
if (nl > max) {
max = nl;
s = w;
}
}
if (s != wrapper) {
//for (sm = wrapper.next.next.next; sm.next != null; sm = sm.next);
sm = wrapper.next.next.next;
while (sm.next != null) sm = sm.next;
//for (sp = wrapper; sp.next != s && sp.next != null; sp = sp.next);
sp = wrapper;
while (sp.next != s && sp.next != null) sp = sp.next;
sm.next = wrapper;
sp.next = null;
wrapper = s;
}
}
var w:Wrapper = wrapper;
var a:Vertex = w.vertex;
w = w.next;
var b:Vertex = w.vertex;
w = w.next;
var c:Vertex = w.vertex;
var abx:Number = b.x - a.x;
var aby:Number = b.y - a.y;
var abz:Number = b.z - a.z;
var acx:Number = c.x - a.x;
var acy:Number = c.y - a.y;
var acz:Number = c.z - a.z;
var nx:Number = acz*aby - acy*abz;
var ny:Number = acx*abz - acz*abx;
var nz:Number = acy*abx - acx*aby;
var nl:Number = nx*nx + ny*ny + nz*nz;
if (nl > 0) {
nl = 1/Math.sqrt(nl);
nx *= nl;
ny *= nl;
nz *= nl;
normalX = nx;
normalY = ny;
normalZ = nz;
}
offset = a.x*nx + a.y*ny + a.z*nz;
}
/*public function destroy():void {
var w:Wrapper = wrapper;
w.vertex = null;
do {
w = w.next;
w.vertex = null;
} while (w.next != null);
w.next = Wrapper.collector;
Wrapper.collector = wrapper;
material = null;
wrapper = null;
//temporary = false;
processNext = null;
next = collector;
collector = this;
}*/
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
package alternativa.engine3d.core {
public class KDNode {
public var negative:KDNode;
public var positive:KDNode;
public var axis:int;
public var coord:Number;
public var minCoord:Number;
public var maxCoord:Number;
public var boundMinX:Number;
public var boundMinY:Number;
public var boundMinZ:Number;
public var boundMaxX:Number;
public var boundMaxY:Number;
public var boundMaxZ:Number;
public var objects:Vector.<Object3D>;
public var objectsLength:int = 0;
public var occluders:Vector.<Object3D>;
public var occludersLength:int = 0;
}
}

View File

@@ -0,0 +1,27 @@
package alternativa.engine3d.core {
public class MipMapping {
/**
* Нет мипмаппинга.
*/
static public const NONE:int = 0;
/**
* Мипмаппинг по удалённости объекта от камеры.
*/
static public const OBJECT_DISTANCE:int = 1;
/**
* Мипмаппинг для каждой грани по удалённости от камеры.
*/
static public const PER_POLYGON_BY_DISTANCE:int = 2;
/**
* Мипмаппинг для каждой грани по перспективному искажению рёбер.
*/
static public const PER_POLYGON_BY_DISTORTION:int = 3;
/**
* Мипмаппинг для каждого пиксела по удалённости от камеры.
*/
static public const PER_PIXEL:int = 4;
}
}

View File

@@ -0,0 +1,87 @@
package alternativa.engine3d.core {
import flash.events.Event;
public class MouseEvent3D extends Event {
/**
* Значение свойства <code>type</code> для объекта события <code>click</code>.
* @eventType click
*/
public static const CLICK:String = "click";
/**
* Значение свойства <code>type</code> для объекта события <code>doubleClick</code>.
* @eventType doubleClick
*/
public static const DOUBLE_CLICK:String = "doubleClick";
/**
* Значение свойства <code>type</code> для объекта события <code>mouseDown</code>.
* @eventType mouseDown
*/
public static const MOUSE_DOWN:String = "mouseDown";
/**
* Значение свойства <code>type</code> для объекта события <code>mouseUp</code>.
* @eventType mouseUp
*/
public static const MOUSE_UP:String = "mouseUp";
/**
* Значение свойства <code>type</code> для объекта события <code>mouseOver</code>.
* @eventType mouseOver
*/
public static const MOUSE_OVER:String = "mouseOver";
/**
* Значение свойства <code>type</code> для объекта события <code>mouseOut</code>.
* @eventType mouseOut
*/
public static const MOUSE_OUT:String = "mouseOut";
/**
* Значение свойства <code>type</code> для объекта события <code>rollOver</code>.
* @eventType rollOver
*/
public static const ROLL_OVER:String = "rollOver";
/**
* Значение свойства <code>type</code> для объекта события <code>rollOut</code>.
* @eventType rollOut
*/
public static const ROLL_OUT:String = "rollOut";
/**
* Значение свойства <code>type</code> для объекта события <code>mouseMove</code>.
* @eventType mouseMove
*/
public static const MOUSE_MOVE:String = "mouseMove";
/**
* Значение свойства <code>type</code> для объекта события <code>mouseWheel</code>.
* @eventType mouseWheel
*/
public static const MOUSE_WHEEL:String = "mouseWheel";
/**
* Объект, с которым связано событие.
*/
private var _target:Object3D;
/**
* Индикатор нажатой (<code>true</code>) или отпущенной (<code>false</code>) клавиши Alt.
*/
public var altKey:Boolean;
/**
* Индикатор нажатой (<code>true</code>) или отпущенной (<code>false</code>) клавиши Control.
*/
public var ctrlKey:Boolean;
/**
* Индикатор нажатой (<code>true</code>) или отпущенной (<code>false</code>) клавиши Shift.
*/
public var shiftKey:Boolean;
/**
* Количество линий прокрутки при вращении колеса мыши.
*/
public var delta:int;
public function MouseEvent3D(type:String, target:Object3D, altKey:Boolean = false, ctrlKey:Boolean = false, shiftKey:Boolean = false, delta:int = 0) {
super(type);
_target = target;
}
override public function get target():Object {
return _target;
}
}
}

View File

@@ -0,0 +1,618 @@
package alternativa.engine3d.core {
import alternativa.engine3d.alternativa3d;
import flash.geom.ColorTransform;
import flash.geom.Matrix3D;
import flash.geom.Vector3D;
import flash.utils.getQualifiedClassName;
use namespace alternativa3d;
/**
* Событие рассылается когда пользователь последовательно нажимает и отпускает левую кнопку мыши над одним и тем же объектом.
* Между нажатием и отпусканием кнопки могут происходить любые другие события.
* @eventType alternativa.engine3d.events.MouseEvent3D.CLICK
*/
[Event (name="click", type="alternativa.engine3d.core.MouseEvent3D")]
/**
* Событие рассылается когда пользователь последовательно 2 раза в нажимает и отпускает левую кнопку мыши над одним и тем же объектом.
* Событие сработает только если время между первым и вторым кликом вписывается в заданный в системе временной интервал.
* @eventType alternativa.engine3d.events.MouseEvent3D.DOUBLE_CLICK
*/
[Event (name="doubleClick", type="alternativa.engine3d.core.MouseEvent3D")]
/**
* Событие рассылается когда пользователь нажимает левую кнопку мыши над объектом.
* @eventType alternativa.engine3d.events.MouseEvent3D.MOUSE_DOWN
*/
[Event (name="mouseDown", type="alternativa.engine3d.core.MouseEvent3D")]
/**
* Событие рассылается когда пользователь отпускает левую кнопку мыши над объектом.
* @eventType alternativa.engine3d.events.MouseEvent3D.MOUSE_UP
*/
[Event (name="mouseUp", type="alternativa.engine3d.core.MouseEvent3D")]
/**
* Событие рассылается когда пользователь наводит курсор мыши на объект.
* @eventType alternativa.engine3d.events.MouseEvent3D.MOUSE_OVER
*/
[Event (name="mouseOver", type="alternativa.engine3d.core.MouseEvent3D")]
/**
* Событие рассылается когда пользователь уводит курсор мыши с объекта.
* @eventType alternativa.engine3d.events.MouseEvent3D.MOUSE_OUT
*/
[Event (name="mouseOut", type="alternativa.engine3d.core.MouseEvent3D")]
/**
* Событие рассылается когда пользователь наводит курсор мыши на объект.
* @eventType alternativa.engine3d.events.MouseEvent3D.ROLL_OVER
*/
[Event (name="rollOver", type="alternativa.engine3d.core.MouseEvent3D")]
/**
* Событие рассылается когда пользователь уводит курсор мыши с объекта.
* @eventType alternativa.engine3d.events.MouseEvent3D.ROLL_OUT
*/
[Event (name="rollOut", type="alternativa.engine3d.core.MouseEvent3D")]
/**
* Событие рассылается когда пользователь перемещает курсор мыши над объектом.
* @eventType alternativa.engine3d.events.MouseEvent3D.MOUSE_MOVE
*/
[Event (name="mouseMove", type="alternativa.engine3d.core.MouseEvent3D")]
/**
* Событие рассылается когда пользователь вращает колесо мыши над объектом.
* @eventType alternativa.engine3d.events.MouseEvent3D.MOUSE_WHEEL
*/
[Event (name="mouseWheel", type="alternativa.engine3d.core.MouseEvent3D")]
/**
* Базовый трёхмерный объект
*/
public class Object3D {
static private const boundVertexList:Vertex = Vertex.createList(8);
public var name:String;
public var x:Number = 0;
public var y:Number = 0;
public var z:Number = 0;
public var rotationX:Number = 0;
public var rotationY:Number = 0;
public var rotationZ:Number = 0;
public var scaleX:Number = 1;
public var scaleY:Number = 1;
public var scaleZ:Number = 1;
public var visible:Boolean = true;
public var alpha:Number = 1;
public var blendMode:String = "normal";
public var colorTransform:ColorTransform = null;
public var filters:Array = null;
public var interactiveAlpha:Number = 0;
public var mouseEnabled:Boolean = true;
public var mouseChildren:Boolean = true;
public var doubleClickEnabled:Boolean = false;
public var useHandCursor:Boolean = false;
public var boundMinX:Number = -1e+22;
public var boundMinY:Number = -1e+22;
public var boundMinZ:Number = -1e+22;
public var boundMaxX:Number = 1e+22;
public var boundMaxY:Number = 1e+22;
public var boundMaxZ:Number = 1e+22;
alternativa3d var _parent:Object3DContainer;
alternativa3d var culling:int = 0;
// Матрица
alternativa3d var ma:Number;
alternativa3d var mb:Number;
alternativa3d var mc:Number;
alternativa3d var md:Number;
alternativa3d var me:Number;
alternativa3d var mf:Number;
alternativa3d var mg:Number;
alternativa3d var mh:Number;
alternativa3d var mi:Number;
alternativa3d var mj:Number;
alternativa3d var mk:Number;
alternativa3d var ml:Number;
// Инверсная матрица
alternativa3d var ima:Number;
alternativa3d var imb:Number;
alternativa3d var imc:Number;
alternativa3d var imd:Number;
alternativa3d var ime:Number;
alternativa3d var imf:Number;
alternativa3d var img:Number;
alternativa3d var imh:Number;
alternativa3d var imi:Number;
alternativa3d var imj:Number;
alternativa3d var imk:Number;
alternativa3d var iml:Number;
alternativa3d var listeners:Object;
public function get parent():Object3DContainer {
return _parent;
}
public function getMatrix():Matrix3D {
var m:Matrix3D = new Matrix3D();
var t:Vector3D = new Vector3D(x, y, z);
var r:Vector3D = new Vector3D(rotationX, rotationY, rotationZ);
var s:Vector3D = new Vector3D(scaleX, scaleY, scaleZ);
var v:Vector.<Vector3D> = new Vector.<Vector3D>();
v[0] = t;
v[1] = r;
v[2] = s;
m.recompose(v);
return m;
}
public function setMatrix(value:Matrix3D):void {
var v:Vector.<Vector3D> = value.decompose();
var t:Vector3D = v[0];
var r:Vector3D = v[1];
var s:Vector3D = v[2];
x = t.x;
y = t.y;
z = t.z;
rotationX = r.x;
rotationY = r.y;
rotationZ = r.z;
scaleX = s.x;
scaleY = s.y;
scaleZ = s.z;
}
public function get position():Vector3D {
return new Vector3D(x, y, z);
}
public function set position(value:Vector3D):void {
x = value.x;
y = value.y;
z = value.z;
}
public function setPositionXYZ(x:Number, y:Number, z:Number):void {
this.x = x;
this.y = y;
this.z = z;
}
public function setRotationXYZ(rx:Number, ry:Number, rz:Number):void {
rotationX = rx;
rotationY = ry;
rotationZ = rz;
}
public function calculateBounds():void {
// Выворачивание баунда
boundMinX = 1e+22;
boundMinY = 1e+22;
boundMinZ = 1e+22;
boundMaxX = -1e+22;
boundMaxY = -1e+22;
boundMaxZ = -1e+22;
// Заполнение баунда
updateBounds(this, null);
// Если баунд вывернут
if (boundMinX > boundMaxX) {
boundMinX = -1e+22;
boundMinY = -1e+22;
boundMinZ = -1e+22;
boundMaxX = 1e+22;
boundMaxY = 1e+22;
boundMaxZ = 1e+22;
}
}
public function removeFromParent():void {
if (_parent != null)
_parent.removeChild(this);
}
alternativa3d function composeMatrix():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;
ma = cosZ*cosYscaleX;
mb = cosZsinY*sinXscaleY - sinZ*cosXscaleY;
mc = cosZsinY*cosXscaleZ + sinZ*sinXscaleZ;
md = x;
me = sinZ*cosYscaleX;
mf = sinZsinY*sinXscaleY + cosZ*cosXscaleY;
mg = sinZsinY*cosXscaleZ - cosZ*sinXscaleZ;
mh = y;
mi = -sinY*scaleX;
mj = cosY*sinXscaleY;
mk = cosY*cosXscaleZ;
ml = z;
}
alternativa3d function appendMatrix(object:Object3D):void {
var a:Number = ma;
var b:Number = mb;
var c:Number = mc;
var d:Number = md;
var e:Number = me;
var f:Number = mf;
var g:Number = mg;
var h:Number = mh;
var i:Number = mi;
var j:Number = mj;
var k:Number = mk;
var l:Number = ml;
ma = object.ma*a + object.mb*e + object.mc*i;
mb = object.ma*b + object.mb*f + object.mc*j;
mc = object.ma*c + object.mb*g + object.mc*k;
md = object.ma*d + object.mb*h + object.mc*l + object.md;
me = object.me*a + object.mf*e + object.mg*i;
mf = object.me*b + object.mf*f + object.mg*j;
mg = object.me*c + object.mf*g + object.mg*k;
mh = object.me*d + object.mf*h + object.mg*l + object.mh;
mi = object.mi*a + object.mj*e + object.mk*i;
mj = object.mi*b + object.mj*f + object.mk*j;
mk = object.mi*c + object.mj*g + object.mk*k;
ml = object.mi*d + object.mj*h + object.mk*l + object.ml;
}
alternativa3d function composeAndAppend(object:Object3D):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;
var a:Number = cosZ*cosYscaleX;
var b:Number = cosZsinY*sinXscaleY - sinZ*cosXscaleY;
var c:Number = cosZsinY*cosXscaleZ + sinZ*sinXscaleZ;
var d:Number = x;
var e:Number = sinZ*cosYscaleX;
var f:Number = sinZsinY*sinXscaleY + cosZ*cosXscaleY;
var g:Number = sinZsinY*cosXscaleZ - cosZ*sinXscaleZ;
var h:Number = y;
var i:Number = -sinY*scaleX;
var j:Number = cosY*sinXscaleY;
var k:Number = cosY*cosXscaleZ;
var l:Number = z;
ma = object.ma*a + object.mb*e + object.mc*i;
mb = object.ma*b + object.mb*f + object.mc*j;
mc = object.ma*c + object.mb*g + object.mc*k;
md = object.ma*d + object.mb*h + object.mc*l + object.md;
me = object.me*a + object.mf*e + object.mg*i;
mf = object.me*b + object.mf*f + object.mg*j;
mg = object.me*c + object.mf*g + object.mg*k;
mh = object.me*d + object.mf*h + object.mg*l + object.mh;
mi = object.mi*a + object.mj*e + object.mk*i;
mj = object.mi*b + object.mj*f + object.mk*j;
mk = object.mi*c + object.mj*g + object.mk*k;
ml = object.mi*d + object.mj*h + object.mk*l + object.ml;
}
alternativa3d function calculateInverseMatrix(object:Object3D):void {
var a:Number = object.ma;
var b:Number = object.mb;
var c:Number = object.mc;
var d:Number = object.md;
var e:Number = object.me;
var f:Number = object.mf;
var g:Number = object.mg;
var h:Number = object.mh;
var i:Number = object.mi;
var j:Number = object.mj;
var k:Number = object.mk;
var l:Number = object.ml;
var det:Number = 1/(-c*f*i + b*g*i + c*e*j - a*g*j - b*e*k + a*f*k);
ima = (-g*j + f*k)*det;
imb = (c*j - b*k)*det;
imc = (-c*f + b*g)*det;
imd = (d*g*j - c*h*j - d*f*k + b*h*k + c*f*l - b*g*l)*det;
ime = (g*i - e*k)*det;
imf = (-c*i + a*k)*det;
img = (c*e - a*g)*det;
imh = (c*h*i - d*g*i + d*e*k - a*h*k - c*e*l + a*g*l)*det;
imi = (-f*i + e*j)*det;
imj = (b*i - a*j)*det;
imk = (-b*e + a*f)*det;
iml = (d*f*i - b*h*i - d*e*j + a*h*j + b*e*l - a*f*l)*det;
}
alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
}
alternativa3d function getGeometry(camera:Camera3D, object:Object3D):Geometry {
return null;
}
alternativa3d function updateBounds(bounds:Object3D, transformation:Object3D = null):void {
}
alternativa3d function split(normalX:Number, normalY:Number, normalZ:Number, offset:Number, threshold:Number):Vector.<Object3D> {
return new Vector.<Object3D>(2);
}
alternativa3d function cullingInCamera(camera:Camera3D, object:Object3D, culling:int):int {
if (camera.occludedAll) return -1;
var numOccluders:int = camera.numOccluders;
var vertex:Vertex;
// Расчёт точек баунда в координатах камеры
if (culling > 0 || numOccluders > 0) {
// Заполнение
vertex = boundVertexList;
vertex.x = boundMinX;
vertex.y = boundMinY;
vertex.z = boundMinZ;
vertex = vertex.next;
vertex.x = boundMaxX;
vertex.y = boundMinY;
vertex.z = boundMinZ;
vertex = vertex.next;
vertex.x = boundMinX;
vertex.y = boundMaxY;
vertex.z = boundMinZ;
vertex = vertex.next;
vertex.x = boundMaxX;
vertex.y = boundMaxY;
vertex.z = boundMinZ;
vertex = vertex.next;
vertex.x = boundMinX;
vertex.y = boundMinY;
vertex.z = boundMaxZ;
vertex = vertex.next;
vertex.x = boundMaxX;
vertex.y = boundMinY;
vertex.z = boundMaxZ;
vertex = vertex.next;
vertex.x = boundMinX;
vertex.y = boundMaxY;
vertex.z = boundMaxZ;
vertex = vertex.next;
vertex.x = boundMaxX;
vertex.y = boundMaxY;
vertex.z = boundMaxZ;
// Трансформация в камеру
for (vertex = boundVertexList; vertex != null; vertex = vertex.next) {
var x:Number = vertex.x;
var y:Number = vertex.y;
var z:Number = vertex.z;
vertex.cameraX = object.ma*x + object.mb*y + object.mc*z + object.md;
vertex.cameraY = object.me*x + object.mf*y + object.mg*z + object.mh;
vertex.cameraZ = object.mi*x + object.mj*y + object.mk*z + object.ml;
}
}
// Куллинг
if (culling > 0) {
var infront:Boolean;
var behind:Boolean;
if (culling & 1) {
var near:Number = camera.nearClipping;
for (vertex = boundVertexList,infront = false,behind = false; vertex != null; vertex = vertex.next) {
if (vertex.cameraZ > near) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
if (!infront) return -1;
} else {
culling &= 62;
}
}
if (culling & 2) {
var far:Number = camera.farClipping;
for (vertex = boundVertexList,infront = false,behind = false; vertex != null; vertex = vertex.next) {
if (vertex.cameraZ < far) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
if (!infront) return -1;
} else {
culling &= 61;
}
}
if (culling & 4) {
for (vertex = boundVertexList,infront = false,behind = false; vertex != null; vertex = vertex.next) {
if (-vertex.cameraX < vertex.cameraZ) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
if (!infront) return -1;
} else {
culling &= 59;
}
}
if (culling & 8) {
for (vertex = boundVertexList,infront = false,behind = false; vertex != null; vertex = vertex.next) {
if (vertex.cameraX < vertex.cameraZ) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
if (!infront) return -1;
} else {
culling &= 55;
}
}
if (culling & 16) {
for (vertex = boundVertexList,infront = false,behind = false; vertex != null; vertex = vertex.next) {
if (-vertex.cameraY < vertex.cameraZ) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
if (!infront) return -1;
} else {
culling &= 47;
}
}
if (culling & 32) {
for (vertex = boundVertexList,infront = false,behind = false; vertex != null; vertex = vertex.next) {
if (vertex.cameraY < vertex.cameraZ) {
infront = true;
if (behind) break;
} else {
behind = true;
if (infront) break;
}
}
if (behind) {
if (!infront) return -1;
} else {
culling &= 31;
}
}
}
// Окклюдинг
if (numOccluders > 0) {
for (var i:int = 0; i < numOccluders; i++) {
for (var plane:Vertex = camera.occluders[i]; plane != null; plane = plane.next) {
for (vertex = boundVertexList; vertex != null; vertex = vertex.next) {
if (plane.cameraX*vertex.cameraX + plane.cameraY*vertex.cameraY + plane.cameraZ*vertex.cameraZ >= 0) break;
}
if (vertex != null) break;
}
if (plane == null) return -1;
}
}
object.culling = culling;
return culling;
}
public function clone():Object3D {
var res:Object3D = new Object3D();
res.cloneBaseProperties(this);
return res;
}
protected function cloneBaseProperties(source:Object3D):void {
name = source.name;
visible = source.visible;
alpha = source.alpha;
blendMode = source.blendMode;
if (source.colorTransform != null) {
colorTransform = new ColorTransform();
colorTransform.concat(source.colorTransform);
}
if (source.filters != null) {
filters = new Array().concat(source.filters);
}
x = source.x;
y = source.y;
z = source.z;
rotationX = source.rotationX;
rotationY = source.rotationY;
rotationZ = source.rotationZ;
scaleX = source.scaleX;
scaleY = source.scaleY;
scaleZ = source.scaleZ;
boundMinX = source.boundMinX;
boundMinY = source.boundMinY;
boundMinZ = source.boundMinZ;
boundMaxX = source.boundMaxX;
boundMaxY = source.boundMaxY;
boundMaxZ = source.boundMaxZ;
}
/**
* Добавляет обработчик события.
* @param type тип события
* @param listener обработчик события
*/
public function addEventListener(type:String, listener:Function):void {
if (listeners == null) listeners = new Object();
var vector:Vector.<Function> = listeners[type];
if (vector == null) {
vector = new Vector.<Function>();
listeners[type] = vector;
}
if (vector.indexOf(listener) < 0) {
vector.push(listener);
}
}
/**
* Удаляет обработчик события.
* @param type тип события
* @param listener обработчик события
*/
public function removeEventListener(type:String, listener:Function):void {
if (listeners != null) {
var vector:Vector.<Function> = listeners[type];
if (vector != null) {
var i:int = vector.indexOf(listener);
if (i >= 0) {
var length:int = vector.length;
for (var j:int = i + 1; j < length; j++,i++) {
vector[i] = vector[j];
}
if (length > 1) {
vector.length = length - 1;
} else {
delete listeners[type];
var key:*;
for (key in listeners) break;
if (!key) listeners = null;
}
}
}
}
}
/**
* Проверяет наличие зарегистрированных обработчиков события указанного типа.
* @param type тип события
* @return <code>true</code> если есть обработчики события указанного типа, иначе <code>false</code>
*/
public function hasEventListener(type:String):Boolean {
return listeners != null && listeners[type];
}
public function toString():String {
var className:String = getQualifiedClassName(this);
return "[" + className.substr(className.indexOf("::") + 2) + " " + name + "]";
}
}
}

View File

@@ -0,0 +1,191 @@
package alternativa.engine3d.core {
import alternativa.engine3d.alternativa3d;
import flash.geom.ColorTransform;
use namespace alternativa3d;
/**
* Базовый контейнер трёхмерных объектов.
* Логика контейнеров и child-parent-отношений идентична логике
* displayObject'ов во Flash.
*/
public class Object3DContainer extends Object3D {
protected var children:Vector.<Object3D> = new Vector.<Object3D>();
protected var _numChildren:int = 0;
protected var visibleChildren:Vector.<Object3D> = new Vector.<Object3D>();
protected var numVisibleChildren:int = 0;
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
var canvas:Canvas;
var debug:int;
// Сбор видимых объектов
numVisibleChildren = 0;
for (var i:int = 0; i < _numChildren; i++) {
var child:Object3D = children[i];
if (child.visible) {
child.composeAndAppend(object);
if (child.cullingInCamera(camera, child, object.culling) >= 0) {
visibleChildren[numVisibleChildren] = child;
numVisibleChildren++;
}
}
}
// Если есть видимые объекты
if (numVisibleChildren > 0) {
// Дебаг
if (camera.debug && (debug = camera.checkInDebug(this)) > 0) {
canvas = parentCanvas.getChildCanvas(object, true, false);
if (debug & Debug.BOUNDS) Debug.drawBounds(camera, canvas, object, boundMinX, boundMinY, boundMinZ, boundMaxX, boundMaxY, boundMaxZ);
}
// Отрисовка
canvas = parentCanvas.getChildCanvas(object, false, true, object.alpha, object.blendMode, object.colorTransform, object.filters);
canvas.numDraws = 0;
// Отрисовка видимых объектов
drawVisibleChildren(camera, object, canvas);
// Если была отрисовка
if (canvas.numDraws > 0) {
canvas.removeChildren(canvas.numDraws);
} else {
parentCanvas.numDraws--;
}
}
}
protected function drawVisibleChildren(camera:Camera3D, object:Object3D, canvas:Canvas):void {
for (var i:int = numVisibleChildren - 1; i >= 0; i--) {
var child:Object3D = visibleChildren[i];
child.draw(camera, child, canvas);
visibleChildren[i] = null;
}
}
override alternativa3d function getGeometry(camera:Camera3D, object:Object3D):Geometry {
var i:int;
var first:Geometry;
var last:Geometry;
var geometry:Geometry;
for (i = 0; i < _numChildren; i++) {
var child:Object3D = children[i];
if (child.visible) {
child.composeAndAppend(object);
if (child.cullingInCamera(camera, child, object.culling) >= 0) {
geometry = child.getGeometry(camera, child);
if (geometry != null) {
if (first != null) {
last.next = geometry;
} else {
first = geometry;
last = geometry;
}
while (last.next != null) {
last = last.next;
}
}
}
}
}
if (object.alpha != 1) {
geometry = first;
while (geometry != null) {
geometry.alpha *= object.alpha;
geometry = geometry.next;
}
}
if (object.blendMode != "normal") {
geometry = first;
while (geometry != null) {
if (geometry.blendMode == "normal") {
geometry.blendMode = object.blendMode;
}
geometry = geometry.next;
}
}
if (object.colorTransform != null) {
geometry = first;
while (geometry != null) {
if (geometry.colorTransform != null) {
var ct:ColorTransform = new ColorTransform(object.colorTransform.redMultiplier, object.colorTransform.greenMultiplier, object.colorTransform.blueMultiplier, object.colorTransform.alphaMultiplier, object.colorTransform.redOffset, object.colorTransform.greenOffset, object.colorTransform.blueOffset, object.colorTransform.alphaOffset);
ct.concat(geometry.colorTransform);
geometry.colorTransform = ct;
} else {
geometry.colorTransform = object.colorTransform;
}
geometry = geometry.next;
}
}
if (object.filters != null) {
geometry = first;
while (geometry != null) {
if (geometry.filters != null) {
var fs:Array = new Array();
var fsLength:int = 0;
var num:int = geometry.filters.length;
for (i = 0; i < num; i++) {
fs[fsLength] = geometry.filters[i];
fsLength++;
}
num = object.filters.length;
for (i = 0; i < num; i++) {
fs[fsLength] = object.filters[i];
fsLength++;
}
geometry.filters = fs;
} else {
geometry.filters = object.filters;
}
geometry = geometry.next;
}
}
return first;
}
override alternativa3d function updateBounds(bounds:Object3D, transformation:Object3D = null):void {
for (var i:int = 0; i < _numChildren; i++) {
var child:Object3D = children[i];
if (transformation != null) {
child.composeAndAppend(transformation);
} else {
child.composeMatrix();
}
child.updateBounds(bounds, child);
}
}
public function addChild(child:Object3D):void {
children[_numChildren] = child;
child._parent = this;
_numChildren++;
}
public function removeChild(child:Object3D):void {
var i:int = children.indexOf(child);
if (i < 0) throw new ArgumentError("Child not found");
_numChildren--;
var j:int = i + 1;
;
while (i < _numChildren) {
children[i] = children[j];
i++;
j++;
}
children.length = _numChildren;
child._parent = null;
}
public function hasChild(child:Object3D):Boolean {
return children.indexOf(child) > -1;
}
public function getChildAt(index:uint):Object3D {
return children[index];
}
public function get numChildren():uint {
return _numChildren;
}
}
}

View File

@@ -0,0 +1,23 @@
package alternativa.engine3d.core {
public class Sorting {
/**
* Грани не сортируются.
*/
static public const NONE:int = 0;
/**
* Грани сортируются по средним Z.
*/
static public const AVERAGE_Z:int = 1;
/**
* Грани при отрисовке образуют BSP-дерево.
*/
static public const DYNAMIC_BSP:int = 2;
/**
* Грани находятся в BSP-дереве.
*/
static public const STATIC_BSP:int = 3;
}
}

View File

@@ -0,0 +1,78 @@
package alternativa.engine3d.core {
public class Vertex {
public var next:Vertex;
public var x:Number = 0;
public var y:Number = 0;
public var z:Number = 0;
public var u:Number = 0;
public var v:Number = 0;
public var cameraX:Number;
public var cameraY:Number;
public var cameraZ:Number;
public var offset:Number;
public var transformID:int = 0;
public var drawID:int = 0;
public var index:int;
public var value:Vertex;
static public var collector:Vertex;
static public function createList(num:int):Vertex {
var res:Vertex = collector;
var last:Vertex;
if (res != null) {
for (last = res; num > 1; last = last.next,num--) {
last.transformID = 0;
last.drawID = 0;
if (last.next == null) {
//for (; num > 1; /*trace("new Vertex"), */last.next = new Vertex(), last = last.next, num--);
while (num > 1) {
last.next = new Vertex();
last = last.next;
num--;
}
break;
}
}
collector = last.next;
last.transformID = 0;
last.drawID = 0;
last.next = null;
} else {
//for (res = new Vertex(), /*trace("new Vertex"), */last = res; num > 1; /*trace("new Vertex"), */last.next = new Vertex(), last = last.next, num--);
res = new Vertex();
last = res;
while (num > 1) {
last.next = new Vertex();
last = last.next;
num--;
}
}
return res;
}
public function create():Vertex {
if (collector != null) {
var res:Vertex = collector;
collector = res.next;
res.next = null;
res.transformID = 0;
res.drawID = 0;
return res;
} else {
//trace("new Vertex");
return new Vertex();
}
}
}
}

View File

@@ -0,0 +1,328 @@
package alternativa.engine3d.core {
import alternativa.engine3d.alternativa3d;
import flash.display.DisplayObject;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.ColorTransform;
import flash.geom.Point;
use namespace alternativa3d;
public class View extends Canvas {
static private const mouse:Point = new Point();
static private const listeners:Vector.<Function> = new Vector.<Function>();
alternativa3d var _width:Number;
alternativa3d var _height:Number;
alternativa3d var _interactive:Boolean = false;
private var altKey:Boolean;
private var ctrlKey:Boolean;
private var shiftKey:Boolean;
private var pressedObject:Object3D;
private var clickedObject:Object3D;
private var overedObject:Object3D;
private var overedBranch:Vector.<Object3D> = new Vector.<Object3D>();
public function View(width:Number, height:Number, interactive:Boolean = false) {
_width = width;
_height = height;
_interactive = interactive;
mouseEnabled = false;
mouseChildren = false;
tabEnabled = false;
tabChildren = false;
addEventListener(Event.ADDED_TO_STAGE, onAddToStage);
addEventListener(Event.REMOVED_FROM_STAGE, onRemoveFromStage);
}
private function onAddToStage(e:Event):void {
if (_interactive) addListeners();
}
private function onRemoveFromStage(e:Event):void {
if (_interactive) removeListeners();
}
public function get interactive():Boolean {
return _interactive;
}
public function set interactive(value:Boolean):void {
if (_interactive != value) {
if (stage != null) {
if (value) {
addListeners();
} else {
removeListeners();
}
}
_interactive = value;
}
}
private function addListeners():void {
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
stage.addEventListener(MouseEvent.CLICK, onClick);
stage.addEventListener(MouseEvent.DOUBLE_CLICK, onDoubleClick);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
stage.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKey);
stage.addEventListener(KeyboardEvent.KEY_UP, onKey);
}
private function removeListeners():void {
stage.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
stage.removeEventListener(MouseEvent.CLICK, onClick);
stage.removeEventListener(MouseEvent.DOUBLE_CLICK, onDoubleClick);
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
stage.removeEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKey);
stage.removeEventListener(KeyboardEvent.KEY_UP, onKey);
pressedObject = null;
clickedObject = null;
overedObject = null;
overedBranch.length = 0;
}
private function onMouseDown(mouseEvent:MouseEvent):void {
altKey = mouseEvent.altKey;
ctrlKey = mouseEvent.ctrlKey;
shiftKey = mouseEvent.shiftKey;
var targetCanvas:Canvas = defineTargetCanvas();
if (targetCanvas != null) {
pressedObject = targetCanvas.interactiveObject;
dispatchEventToCanvasHierarchy(MouseEvent3D.MOUSE_DOWN, targetCanvas);
}
}
private function onClick(mouseEvent:MouseEvent):void {
altKey = mouseEvent.altKey;
ctrlKey = mouseEvent.ctrlKey;
shiftKey = mouseEvent.shiftKey;
var targetCanvas:Canvas = defineTargetCanvas();
if (targetCanvas != null) {
dispatchEventToCanvasHierarchy(MouseEvent3D.MOUSE_UP, targetCanvas);
if (pressedObject == targetCanvas.interactiveObject) {
clickedObject = targetCanvas.interactiveObject;
dispatchEventToCanvasHierarchy(MouseEvent3D.CLICK, targetCanvas);
}
}
pressedObject = null;
}
private function onDoubleClick(mouseEvent:MouseEvent):void {
altKey = mouseEvent.altKey;
ctrlKey = mouseEvent.ctrlKey;
shiftKey = mouseEvent.shiftKey;
var targetCanvas:Canvas = defineTargetCanvas();
if (targetCanvas != null) {
dispatchEventToCanvasHierarchy(MouseEvent3D.MOUSE_UP, targetCanvas);
if (pressedObject == targetCanvas.interactiveObject) {
dispatchEventToCanvasHierarchy(clickedObject == targetCanvas.interactiveObject && targetCanvas.interactiveObject.doubleClickEnabled ? MouseEvent3D.DOUBLE_CLICK : MouseEvent3D.CLICK, targetCanvas);
}
}
clickedObject = null;
pressedObject = null;
}
alternativa3d function onMouseMove(mouseEvent:MouseEvent = null):void {
if (mouseEvent != null) {
altKey = mouseEvent.altKey;
ctrlKey = mouseEvent.ctrlKey;
shiftKey = mouseEvent.shiftKey;
}
var targetCanvas:Canvas = defineTargetCanvas();
if (targetCanvas != null) {
var i:int;
var canvas:Canvas;
var object:Object3D;
if (overedObject != targetCanvas.interactiveObject) {
if (overedObject != null) {
var length:int = overedBranch.length;
dispatchEventToObjectHierarchy(MouseEvent3D.MOUSE_OUT);
for (i = 0; i < length; i++) {
object = overedBranch[i];
canvas = targetCanvas;
while (canvas != this) {
if (object == canvas.interactiveObject) break;
canvas = Canvas(canvas.parent);
}
if (canvas == this) {
dispatchEvent3D(object, MouseEvent3D.ROLL_OUT, object);
}
}
canvas = targetCanvas;
while (canvas != this) {
object = canvas.interactiveObject;
for (i = 0; i < length; i++) {
if (object == overedBranch[i]) break;
}
if (i == length) {
dispatchEvent3D(object, MouseEvent3D.ROLL_OVER, object);
}
canvas = Canvas(canvas.parent);
}
} else {
dispatchEventToCanvasHierarchy(MouseEvent3D.ROLL_OVER, targetCanvas);
}
dispatchEventToCanvasHierarchy(MouseEvent3D.MOUSE_OVER, targetCanvas);
}
if (mouseEvent != null) {
dispatchEventToCanvasHierarchy(MouseEvent3D.MOUSE_MOVE, targetCanvas);
}
i = 0;
canvas = targetCanvas;
while (canvas != this) {
overedBranch[i] = canvas.interactiveObject;
i++;
canvas = Canvas(canvas.parent);
}
overedObject = targetCanvas.interactiveObject;
overedBranch.length = i;
} else if (overedObject != null) {
dispatchEventToObjectHierarchy(MouseEvent3D.MOUSE_OUT);
dispatchEventToObjectHierarchy(MouseEvent3D.ROLL_OUT);
overedObject = null;
overedBranch.length = 0;
}
}
private function onMouseWheel(mouseEvent:MouseEvent):void {
altKey = mouseEvent.altKey;
ctrlKey = mouseEvent.ctrlKey;
shiftKey = mouseEvent.shiftKey;
var targetCanvas:Canvas = defineTargetCanvas();
if (targetCanvas != null) {
dispatchEventToCanvasHierarchy(MouseEvent3D.MOUSE_WHEEL, targetCanvas, mouseEvent.delta);
}
}
private function dispatchEventToCanvasHierarchy(type:String, canvas:Canvas, delta:int = 0):void {
var target:Object3D = canvas.interactiveObject;
while (canvas != this) {
dispatchEvent3D(canvas.interactiveObject, type, target, delta);
canvas = Canvas(canvas.parent);
}
}
private function dispatchEventToObjectHierarchy(type:String, delta:int = 0):void {
var target:Object3D = overedBranch[0];
var length:int = overedBranch.length;
for (var i:int = 0; i < length; i++) {
dispatchEvent3D(overedBranch[i], type, target, delta);
}
}
private function dispatchEvent3D(object:Object3D, type:String, target:Object3D, delta:int = 0):void {
if (object.listeners != null) {
var vector:Vector.<Function> = object.listeners[type];
if (vector != null) {
var i:int;
var length:int = vector.length;
for (i = 0; i < length; i++) {
listeners[i] = vector[i];
}
for (i = 0; i < length; i++) {
(listeners[i] as Function).call(null, new MouseEvent3D(type, target, altKey, ctrlKey, shiftKey, delta));
}
}
}
}
private function defineTargetCanvas():Canvas {
// Если мышь внутри области вьюпорта
if (mouseX >= 0 && mouseY >= 0 && mouseX <= _width && mouseY <= _height) {
// Получение объектов под мышью
mouse.x = stage.mouseX;
mouse.y = stage.mouseY;
var displayObjects:Array = stage.getObjectsUnderPoint(mouse);
// Перебор объектов
for (var i:int = displayObjects.length - 1; i >= 0; i--) {
var displayObject:DisplayObject = displayObjects[i];
// Поиск канваса
while (displayObject != null && !(displayObject is Canvas)) {
displayObject = displayObject.parent;
}
// Если канвас найден
if (displayObject != null) {
var canvas:Canvas = Canvas(displayObject);
// Проверка на прозрачность
if (canvas.interactiveObject.interactiveAlpha <= 1) {
// TODO: проверка порога альфы if (canvas.interactiveObject.interactiveAlpha > 0)
// Определение целевого объекта
var target:Canvas = null;
while (canvas != this) {
if (!canvas.interactiveObject.mouseChildren) {
target = null;
}
if (target == null && canvas.interactiveObject.mouseEnabled) {
target = canvas;
}
canvas = Canvas(canvas.parent);
}
if (target != null) {
return target;
}
}
} else {
// TODO: тут должна быть проверка на интерактивность, чтобы понять перекрывает что-то вьюпорт или нет
}
}
}
return null;
}
private function onKey(keyboardEvent:KeyboardEvent):void {
altKey = keyboardEvent.altKey;
ctrlKey = keyboardEvent.ctrlKey;
shiftKey = keyboardEvent.shiftKey;
}
override public function get width():Number {
return _width;
}
override public function set width(value:Number):void {
_width = value;
}
override public function get height():Number {
return _height;
}
override public function set height(value:Number):void {
_height = value;
}
public function clear():void {
removeChildren(0);
numDraws = 0;
}
override alternativa3d function getChildCanvas(interactiveObject:Object3D, useGraphics:Boolean, useChildren:Boolean, alpha:Number = 1, blendMode:String = "normal", colorTransform:ColorTransform = null, filters:Array = null):Canvas {
var canvas:Canvas = super.getChildCanvas(interactiveObject, useGraphics, useChildren, alpha, blendMode, colorTransform, filters);
canvas.x = _width/2;
canvas.y = _height/2;
return canvas;
}
override alternativa3d function removeChildren(keep:int):void {
for (var i:int = 0; i < _numChildren - keep; i++) {
var canvas:Canvas = getChildAt(i) as Canvas;
if (canvas != null) {
canvas.x = 0;
canvas.y = 0;
}
}
super.removeChildren(keep);
}
}
}

View File

@@ -0,0 +1,36 @@
package alternativa.engine3d.core {
public class Wrapper {
public var next:Wrapper;
public var vertex:Vertex;
static public var collector:Wrapper;
static public function create():Wrapper {
if (collector != null) {
var res:Wrapper = collector;
collector = collector.next;
res.next = null;
return res;
} else {
//trace("new Wrapper");
return new Wrapper();
}
}
public function create():Wrapper {
if (collector != null) {
var res:Wrapper = collector;
collector = collector.next;
res.next = null;
return res;
} else {
//trace("new Wrapper");
return new Wrapper();
}
}
}
}

View File

@@ -0,0 +1,230 @@
package alternativa.engine3d.loaders {
import alternativa.engine3d.loaders.events.LoaderErrorEvent;
import alternativa.engine3d.loaders.events.LoaderEvent;
import alternativa.engine3d.loaders.events.LoaderProgressEvent;
import alternativa.engine3d.materials.TextureMaterial;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BitmapDataChannel;
import flash.display.BlendMode;
import flash.display.Loader;
import flash.events.ErrorEvent;
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.geom.Point;
import flash.net.URLRequest;
import flash.system.LoaderContext;
/**
* Рассылается вначале загрузки очередного материала.
*
* @eventType alternativa.engine3d.loaders.events.LoaderEvent.PART_OPEN
*/
[Event (name="partOpen", type="alternativa.engine3d.loaders.events.LoaderEvent")]
/**
* Рассылается после окончания этапа очередного материала.
* В событии в свойстве target содержится загруженный материал.
*
* @eventType alternativa.engine3d.loaders.events.LoaderEvent.PART_COMPLETE
*/
[Event (name="partComplete", type="alternativa.engine3d.loaders.events.LoaderEvent")]
/**
* Рассылается после окончания загрузки всех материалов.
*
* @eventType flash.events.Event.COMPLETE
*/
[Event (name="complete", type="flash.events.Event")]
/**
* Рассылается, если в процессе загрузки возникает ошибка.
*
* @eventType alternativa.engine3d.loaders.events.LoaderErrorEvent.LOADER_ERROR
*/
[Event (name="loaderError", type="alternativa.engine3d.loaders.events.LoaderErrorEvent")]
/**
* Загрузчик текстур материалов
*/
public class MaterialLoader extends EventDispatcher {
static private var stub:BitmapData;
private var loader:Loader;
private var context:LoaderContext;
private var materials:Vector.<TextureMaterial>;
private var urls:Vector.<String>;
private var filesTotal:int;
private var filesLoaded:int;
private var diffuse:BitmapData;
private var currentURL:String;
private var index:int;
/**
* Начинает загрузку текстур материалов
*
* @param materials список материалов для загрузки их текстур
* @param context
*/
public function load(materials:Vector.<TextureMaterial>, context:LoaderContext = null):void {
this.context = context;
this.materials = materials;
urls = new Vector.<String>();
for (var i:int = 0, j:int = 0; i < materials.length; i++) {
var material:TextureMaterial = materials[i];
urls[j++] = material.diffuseMapURL;
filesTotal++;
if (material.opacityMapURL != null) {
urls[j++] = material.opacityMapURL;
filesTotal++;
} else {
urls[j++] = null;
}
}
filesLoaded = 0;
index = -1;
loadNext(null);
}
/**
* Останавливает загрузку и выполняет зачистку загрузчика.
*/
public function close():void {
destroyLoader();
materials = null;
urls = null;
diffuse = null;
currentURL = null;
context = null;
}
private function destroyLoader():void {
if (loader != null) {
loader.unload();
loader.contentLoaderInfo.removeEventListener(Event.OPEN, onPartOpen);
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loadNext);
loader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, onFileProgress);
loader.contentLoaderInfo.removeEventListener(IOErrorEvent.DISK_ERROR, loadNext);
loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, loadNext);
loader.contentLoaderInfo.removeEventListener(IOErrorEvent.NETWORK_ERROR, loadNext);
loader.contentLoaderInfo.removeEventListener(IOErrorEvent.VERIFY_ERROR, loadNext);
loader.contentLoaderInfo.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, loadNext);
loader = null;
}
}
private function loadNext(e:Event):void {
if (index >= 0) {
if (index % 2 == 0) {
// Завершение загрузки диффузии
if (e is ErrorEvent) {
diffuse = getStub();
onFileError((e as ErrorEvent).text);
} else {
diffuse = (loader.content as Bitmap).bitmapData;
}
filesLoaded++;
} else {
// Завершение загрузки альфы
var material:TextureMaterial = materials[(index - 1) >> 1];
if (e == null) {
material.texture = diffuse;
} else {
if (e is ErrorEvent) {
material.texture = diffuse;
onFileError((e as ErrorEvent).text);
} else {
material.texture = merge(diffuse, (loader.content as Bitmap).bitmapData);
}
filesLoaded++;
}
onPartComplete((index - 1) >> 1, material);
diffuse = null;
}
destroyLoader();
}
if (++index >= urls.length) {
// Завершение всей загрузки
close();
if (hasEventListener(Event.COMPLETE)) {
dispatchEvent(new Event(Event.COMPLETE));
}
} else {
// Загрузка следующего файла
currentURL = urls[index];
if (currentURL != null && (diffuse == null || diffuse != stub)) {
loader = new Loader();
if (index % 2 == 0) {
loader.contentLoaderInfo.addEventListener(Event.OPEN, onPartOpen);
}
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadNext);
loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onFileProgress);
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(currentURL), context);
} else {
loadNext(null);
}
}
}
private function onPartOpen(e:Event):void {
if (hasEventListener(LoaderEvent.PART_OPEN)) {
dispatchEvent(new LoaderEvent(LoaderEvent.PART_OPEN, urls.length >> 1, index >> 1, materials[index >> 1]));
}
}
private function onPartComplete(partsLoaded:int, material:TextureMaterial):void {
if (hasEventListener(LoaderEvent.PART_COMPLETE)) {
dispatchEvent(new LoaderEvent(LoaderEvent.PART_COMPLETE, urls.length >> 1, partsLoaded, material));
}
}
private function onFileProgress(e:ProgressEvent):void {
if (hasEventListener(LoaderProgressEvent.LOADER_PROGRESS)) {
dispatchEvent(new LoaderProgressEvent(LoaderProgressEvent.LOADER_PROGRESS, filesTotal, filesLoaded, (filesLoaded + e.bytesLoaded/e.bytesTotal)/filesTotal, e.bytesLoaded, e.bytesTotal));
}
}
private function onFileError(text:String):void {
dispatchEvent(new LoaderErrorEvent(LoaderErrorEvent.LOADER_ERROR, currentURL, text));
}
private function merge(diffuse:BitmapData, alpha:BitmapData):BitmapData {
var res:BitmapData = new BitmapData(diffuse.width, diffuse.height);
res.copyPixels(diffuse, diffuse.rect, new Point());
if (diffuse.width != alpha.width || diffuse.height != alpha.height) {
diffuse.draw(alpha, new Matrix(diffuse.width/alpha.width, 0, 0, diffuse.height/alpha.height), null, BlendMode.NORMAL, null, true);
alpha.dispose();
alpha = diffuse;
} else {
diffuse.dispose();
}
res.copyChannel(alpha, alpha.rect, new Point(), BitmapDataChannel.RED, BitmapDataChannel.ALPHA);
alpha.dispose();
return res;
}
private function getStub():BitmapData {
if (stub == null) {
var size:uint = 20;
stub = new BitmapData(size, size, false, 0);
for (var i:uint = 0; i < size; i++) {
for (var j:uint = 0; j < size; j += 2) {
stub.setPixel((i % 2) ? j : (j + 1), i, 0xFF00FF);
}
}
}
return stub;
}
}
}

View File

@@ -0,0 +1,780 @@
package alternativa.engine3d.loaders {
import alternativa.engine3d.core.Face;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Vertex;
import alternativa.engine3d.core.Wrapper;
import alternativa.engine3d.materials.FillMaterial;
import alternativa.engine3d.materials.Material;
import alternativa.engine3d.materials.TextureMaterial;
import alternativa.engine3d.objects.Mesh;
import flash.geom.Matrix;
import flash.geom.Vector3D;
import flash.utils.ByteArray;
import flash.utils.Endian;
public class Parser3DS {
private static const CHUNK_MAIN:int = 0x4D4D;
private static const CHUNK_VERSION:int = 0x0002;
private static const CHUNK_SCENE:int = 0x3D3D;
private static const CHUNK_ANIMATION:int = 0xB000;
private static const CHUNK_OBJECT:int = 0x4000;
private static const CHUNK_TRIMESH:int = 0x4100;
private static const CHUNK_VERTICES:int = 0x4110;
private static const CHUNK_FACES:int = 0x4120;
private static const CHUNK_FACESMATERIAL:int = 0x4130;
private static const CHUNK_MAPPINGCOORDS:int = 0x4140;
//private static const CHUNK_OBJECTCOLOR:int = 0x4165;
private static const CHUNK_TRANSFORMATION:int = 0x4160;
//private static const CHUNK_MESHANIMATION:int = 0xB002;
private static const CHUNK_MATERIAL:int = 0xAFFF;
private var data:ByteArray;
private var objectDatas:Object;
private var animationDatas:Array;
private var materialDatas:Object;
public var objects:Vector.<Object3D>;
public var parents:Vector.<Object3D>;
public var materials:Vector.<Material>;
public var textureMaterials:Vector.<TextureMaterial>;
public function parse(data:ByteArray, texturesBaseURL:String = "", scale:Number = 1):void {
if (data.bytesAvailable < 6) return;
this.data = data;
data.endian = Endian.LITTLE_ENDIAN;
parse3DSChunk(data.position, data.bytesAvailable);
objects = new Vector.<Object3D>();
parents = new Vector.<Object3D>();
materials = new Vector.<Material>();
textureMaterials = new Vector.<TextureMaterial>();
buildContent(texturesBaseURL, scale);
data = null;
objectDatas = null;
animationDatas = null;
materialDatas = null;
}
private function readChunkInfo(dataPosition:int):ChunkInfo {
data.position = dataPosition;
var chunkInfo:ChunkInfo = new ChunkInfo();
chunkInfo.id = data.readUnsignedShort();
chunkInfo.size = data.readUnsignedInt();
chunkInfo.dataSize = chunkInfo.size - 6;
chunkInfo.dataPosition = data.position;
chunkInfo.nextChunkPosition = dataPosition + chunkInfo.size;
return chunkInfo;
}
private function parse3DSChunk(dataPosition:int, bytesAvailable:int):void {
if (bytesAvailable < 6) return;
var chunkInfo:ChunkInfo = readChunkInfo(dataPosition);
data.position = dataPosition;
switch (chunkInfo.id) {
// Главный
case CHUNK_MAIN:
parseMainChunk(chunkInfo.dataPosition, chunkInfo.dataSize);
break;
}
parse3DSChunk(chunkInfo.nextChunkPosition, bytesAvailable - chunkInfo.size);
}
private function parseMainChunk(dataPosition:int, bytesAvailable:int):void {
if (bytesAvailable < 6) return;
var chunkInfo:ChunkInfo = readChunkInfo(dataPosition);
switch (chunkInfo.id) {
// Версия
case CHUNK_VERSION:
//version = data.readUnsignedInt();
break;
// 3D-сцена
case CHUNK_SCENE:
parse3DChunk(chunkInfo.dataPosition, chunkInfo.dataSize);
break;
// Анимация
case CHUNK_ANIMATION:
parseAnimationChunk(chunkInfo.dataPosition, chunkInfo.dataSize);
break;
}
parseMainChunk(chunkInfo.nextChunkPosition, bytesAvailable - chunkInfo.size);
}
private function parse3DChunk(dataPosition:int, bytesAvailable:int):void {
if (bytesAvailable < 6) return;
var chunkInfo:ChunkInfo = readChunkInfo(dataPosition);
switch (chunkInfo.id) {
// Материал
case CHUNK_MATERIAL:
// Парсим материал
var material:MaterialData = new MaterialData();
parseMaterialChunk(material, chunkInfo.dataPosition, chunkInfo.dataSize);
break;
// Объект
case CHUNK_OBJECT:
parseObject(chunkInfo);
break;
}
parse3DChunk(chunkInfo.nextChunkPosition, bytesAvailable - chunkInfo.size);
}
private function parseObject(chunkInfo:ChunkInfo):void {
// Создаём список объектов, если надо
if (objectDatas == null) {
objectDatas = new Object();
}
// Создаём данные объекта
var object:ObjectData = new ObjectData();
// Получаем название объекта
object.name = getString(chunkInfo.dataPosition);
// Помещаем данные объекта в список
objectDatas[object.name] = object;
// Парсим объект
var offset:int = object.name.length + 1;
parseObjectChunk(object, chunkInfo.dataPosition + offset, chunkInfo.dataSize - offset);
}
private function parseObjectChunk(object:ObjectData, dataPosition:int, bytesAvailable:int):void {
if (bytesAvailable < 6) return;
var chunkInfo:ChunkInfo = readChunkInfo(dataPosition);
switch (chunkInfo.id) {
// Меш
case CHUNK_TRIMESH:
parseMeshChunk(object, chunkInfo.dataPosition, chunkInfo.dataSize);
break;
// Источник света
case 0x4600:
break;
// Камера
case 0x4700:
break;
}
parseObjectChunk(object, chunkInfo.nextChunkPosition, bytesAvailable - chunkInfo.size);
}
private function parseMeshChunk(object:ObjectData, dataPosition:int, bytesAvailable:int):void {
if (bytesAvailable < 6) return;
var chunkInfo:ChunkInfo = readChunkInfo(dataPosition);
switch (chunkInfo.id) {
// Вершины
case CHUNK_VERTICES:
parseVertices(object);
break;
// UV
case CHUNK_MAPPINGCOORDS:
parseUVs(object);
break;
// Трансформация
case CHUNK_TRANSFORMATION:
parseMatrix(object);
break;
// Грани
case CHUNK_FACES:
parseFaces(object, chunkInfo);
break;
}
parseMeshChunk(object, chunkInfo.nextChunkPosition, bytesAvailable - chunkInfo.size);
}
private function parseVertices(object:ObjectData):void {
var num:int = data.readUnsignedShort();
object.vertices = new Vector.<Number>();
for (var i:int = 0, j:int = 0; i < num; i++) {
object.vertices[j++] = data.readFloat();
object.vertices[j++] = data.readFloat();
object.vertices[j++] = data.readFloat();
}
}
private function parseUVs(object:ObjectData):void {
var num:int = data.readUnsignedShort();
object.uvs = new Vector.<Number>();
for (var i:int = 0, j:int = 0; i < num; i++) {
object.uvs[j++] = data.readFloat();
object.uvs[j++] = data.readFloat();
}
}
private function parseMatrix(object:ObjectData):void {
object.a = data.readFloat();
object.e = data.readFloat();
object.i = data.readFloat();
object.b = data.readFloat();
object.f = data.readFloat();
object.j = data.readFloat();
object.c = data.readFloat();
object.g = data.readFloat();
object.k = data.readFloat();
object.d = data.readFloat();
object.h = data.readFloat();
object.l = data.readFloat();
}
private function parseFaces(object:ObjectData, chunkInfo:ChunkInfo):void {
var num:int = data.readUnsignedShort();
object.faces = new Vector.<int>();
for (var i:int = 0, j:int = 0; i < num; i++) {
object.faces[j++] = data.readUnsignedShort();
object.faces[j++] = data.readUnsignedShort();
object.faces[j++] = data.readUnsignedShort();
data.position += 2; // Пропускаем флаг отрисовки рёбер
}
var offset:int = 2 + 8*num;
parseFacesChunk(object, chunkInfo.dataPosition + offset, chunkInfo.dataSize - offset);
}
private function parseFacesChunk(object:ObjectData, dataPosition:int, bytesAvailable:int):void {
if (bytesAvailable < 6) return;
var chunkInfo:ChunkInfo = readChunkInfo(dataPosition);
switch (chunkInfo.id) {
// Поверхности
case CHUNK_FACESMATERIAL:
parseSurface(object);
break;
}
parseFacesChunk(object, chunkInfo.nextChunkPosition, bytesAvailable - chunkInfo.size);
}
private function parseSurface(object:ObjectData):void {
// Создаём список поверхностей, если надо
if (object.surfaces == null) {
object.surfaces = new Object();
}
// Создаём данные поверхности
var surface:Vector.<int> = new Vector.<int>;
// Помещаем данные поверхности в список
object.surfaces[getString(data.position)] = surface;
// Получаем грани поверхности
var num:int = data.readUnsignedShort();
for (var i:int = 0; i < num; i++) {
surface[i] = data.readUnsignedShort();
}
}
private function parseAnimationChunk(dataPosition:int, bytesAvailable:int):void {
if (bytesAvailable < 6) return;
var chunkInfo:ChunkInfo = readChunkInfo(dataPosition);
switch (chunkInfo.id) {
// Анимация объекта
case 0xB001:
case 0xB002:
case 0xB003:
case 0xB004:
case 0xB005:
case 0xB006:
case 0xB007:
if (animationDatas == null) {
animationDatas = new Array();
}
var animation:AnimationData = new AnimationData();
animationDatas.push(animation);
parseObjectAnimationChunk(animation, chunkInfo.dataPosition, chunkInfo.dataSize);
break;
// Таймлайн
case 0xB008:
break;
}
parseAnimationChunk(chunkInfo.nextChunkPosition, bytesAvailable - chunkInfo.size);
}
private function parseObjectAnimationChunk(animation:AnimationData, dataPosition:int, bytesAvailable:int):void {
if (bytesAvailable < 6) return;
var chunkInfo:ChunkInfo = readChunkInfo(dataPosition);
switch (chunkInfo.id) {
// Идентификация объекта и его связь
case 0xB010:
// Имя объекта
animation.objectName = getString(data.position);
data.position += 4;
// Индекс родительского объекта в линейном списке объектов сцены
animation.parentIndex = data.readUnsignedShort();
break;
// Имя dummy объекта
case 0xB011:
animation.objectName = getString(data.position);
break;
// Точка привязки объекта (pivot)
case 0xB013:
animation.pivot = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
break;
// Смещение объекта относительно родителя
case 0xB020:
data.position += 20;
animation.position = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
break;
// Поворот объекта относительно родителя (angle-axis)
case 0xB021:
data.position += 20;
animation.rotation = getRotationFrom3DSAngleAxis(data.readFloat(), data.readFloat(), data.readFloat(), data.readFloat());
break;
// Масштабирование объекта относительно родителя
case 0xB022:
data.position += 20;
animation.scale = new Vector3D(data.readFloat(), data.readFloat(), data.readFloat());
break;
}
parseObjectAnimationChunk(animation, chunkInfo.nextChunkPosition, bytesAvailable - chunkInfo.size);
}
private function parseMaterialChunk(material:MaterialData, dataPosition:int, bytesAvailable:int):void {
if (bytesAvailable < 6) return;
var chunkInfo:ChunkInfo = readChunkInfo(dataPosition);
switch (chunkInfo.id) {
// Имя материала
case 0xA000:
parseMaterialName(material);
break;
// Ambient color
case 0xA010:
break;
// Diffuse color
case 0xA020:
data.position = chunkInfo.dataPosition + 6;
material.color = (data.readUnsignedByte() << 16) + (data.readUnsignedByte() << 8) + data.readUnsignedByte();
break;
// Specular color
case 0xA030:
break;
// Shininess percent
case 0xA040:
data.position = chunkInfo.dataPosition + 6;
material.glossiness = data.readUnsignedShort();
break;
// Shininess strength percent
case 0xA041:
data.position = chunkInfo.dataPosition + 6;
material.specular = data.readUnsignedShort();
break;
// Transperensy
case 0xA050:
data.position = chunkInfo.dataPosition + 6;
material.transparency = data.readUnsignedShort();
break;
// Texture map 1
case 0xA200:
material.diffuseMap = new MapData();
parseMapChunk(material.name, material.diffuseMap, chunkInfo.dataPosition, chunkInfo.dataSize);
break;
// Texture map 2
case 0xA33A:
break;
// Opacity map
case 0xA210:
material.opacityMap = new MapData();
parseMapChunk(material.name, material.opacityMap, chunkInfo.dataPosition, chunkInfo.dataSize);
break;
// Bump map
case 0xA230:
break;
// Shininess map
case 0xA33C:
break;
// Specular map
case 0xA204:
break;
// Self-illumination map
case 0xA33D:
break;
// Reflection map
case 0xA220:
break;
}
parseMaterialChunk(material, chunkInfo.nextChunkPosition, bytesAvailable - chunkInfo.size);
}
private function parseMaterialName(material:MaterialData):void {
// Создаём список материалов, если надо
if (materialDatas == null) {
materialDatas = new Object();
}
// Получаем название материала
material.name = getString(data.position);
// Помещаем данные материала в список
materialDatas[material.name] = material;
}
private function parseMapChunk(materialName:String, map:MapData, dataPosition:int, bytesAvailable:int):void {
if (bytesAvailable < 6) return;
var chunkInfo:ChunkInfo = readChunkInfo(dataPosition);
switch (chunkInfo.id) {
// Имя файла
case 0xA300:
map.filename = getString(chunkInfo.dataPosition).toLowerCase();
break;
case 0xA351:
// Параметры текстурирования
//trace("MAP OPTIONS", data.readShort().toString(2));
break;
// Масштаб по U
case 0xA354:
map.scaleU = data.readFloat();
break;
// Масштаб по V
case 0xA356:
map.scaleV = data.readFloat();
break;
// Смещение по U
case 0xA358:
map.offsetU = data.readFloat();
break;
// Смещение по V
case 0xA35A:
map.offsetV = data.readFloat();
break;
// Угол поворота
case 0xA35C:
map.rotation = data.readFloat();
break;
}
parseMapChunk(materialName, map, chunkInfo.nextChunkPosition, bytesAvailable - chunkInfo.size);
}
private function buildContent(texturesBaseURL:String, scale:Number):void {
// Расчёт матриц текстурных материалов
for (var materialName:String in materialDatas) {
var materialData:MaterialData = materialDatas[materialName];
var mapData:MapData = materialData.diffuseMap;
if (mapData != null) {
var materialMatrix:Matrix = new Matrix();
var rot:Number = mapData.rotation*Math.PI/180;
materialMatrix.translate(-mapData.offsetU, mapData.offsetV);
materialMatrix.translate(-0.5, -0.5);
materialMatrix.rotate(-rot);
materialMatrix.scale(mapData.scaleU, mapData.scaleV);
materialMatrix.translate(0.5, 0.5);
materialData.matrix = materialMatrix;
var textureMaterial:TextureMaterial = new TextureMaterial();
textureMaterial.name = materialName;
textureMaterial.diffuseMapURL = texturesBaseURL + mapData.filename;
textureMaterial.opacityMapURL = (materialData.opacityMap != null) ? (texturesBaseURL + materialData.opacityMap.filename) : null;
materialData.material = textureMaterial;
textureMaterial.name = materialData.name;
textureMaterials.push(textureMaterial);
} else {
var fillMaterial:FillMaterial = new FillMaterial(materialData.color);
materialData.material = fillMaterial;
fillMaterial.name = materialData.name;
}
materials.push(materialData.material);
}
var objectName:String;
var objectData:ObjectData;
var object:Object3D;
// В сцене есть иерархически связанные оьъекты и (или) указаны данные о трансформации объектов.
if (animationDatas != null) {
if (objectDatas != null) {
var i:int;
var length:int = animationDatas.length;
var animationData:AnimationData;
for (i = 0; i < length; i++) {
animationData = animationDatas[i];
objectName = animationData.objectName;
objectData = objectDatas[objectName];
// Проверка на инстансы
if (objectData != null) {
for (var j:int = i + 1, nameCounter:int = 1; j < length; j++) {
var animationData2:AnimationData = animationDatas[j];
if (!animationData2.isInstance && objectName == animationData2.objectName) {
// Найдено совпадение имени объекта в проверяемой секции анимации. Создаём референс.
var newObjectData:ObjectData = new ObjectData();
var newName:String = objectName + nameCounter++;
newObjectData.name = newName;
objectDatas[newName] = newObjectData;
animationData2.objectName = newName;
newObjectData.vertices = objectData.vertices;
newObjectData.uvs = objectData.uvs;
newObjectData.faces = objectData.faces;
newObjectData.surfaces = objectData.surfaces;
newObjectData.a = objectData.a;
newObjectData.b = objectData.b;
newObjectData.c = objectData.c;
newObjectData.d = objectData.d;
newObjectData.e = objectData.e;
newObjectData.f = objectData.f;
newObjectData.g = objectData.g;
newObjectData.h = objectData.h;
newObjectData.i = objectData.i;
newObjectData.j = objectData.j;
newObjectData.k = objectData.k;
newObjectData.l = objectData.l;
}
}
}
// Если меш
if (objectData != null && objectData.vertices != null) {
// Создание полигонального объекта
object = new Mesh();
buildMesh(object as Mesh, objectData, animationData, scale);
} else {
// Создание пустого 3д-объекта
object = new Object3D();
}
object.name = objectName;
animationData.object = object;
if (animationData.position != null) {
object.x = animationData.position.x*scale;
object.y = animationData.position.y*scale;
object.z = animationData.position.z*scale;
}
if (animationData.rotation != null) {
object.rotationX = animationData.rotation.x;
object.rotationY = animationData.rotation.y;
object.rotationZ = animationData.rotation.z;
}
if (animationData.scale != null) {
object.scaleX = animationData.scale.x;
object.scaleY = animationData.scale.y;
object.scaleZ = animationData.scale.z;
}
}
// Добавление объектов
for (i = 0; i < length; i++) {
animationData = animationDatas[i];
objects.push(animationData.object);
parents.push((animationData.parentIndex == 0xFFFF) ? null : AnimationData(animationDatas[animationData.parentIndex]).object);
}
}
// В сцене нет иерархически связанных объектов и не заданы трансформации для объектов. В контейнер добавляются только полигональные объекты.
} else {
for (objectName in objectDatas) {
objectData = objectDatas[objectName];
if (objectData.vertices != null) {
object = new Mesh();
object.name = objectName;
buildMesh(object as Mesh, objectData, null, scale);
objects.push(object);
parents.push(null);
}
}
}
}
private function buildMesh(mesh:Mesh, objectData:ObjectData, animationData:AnimationData, scale:Number):void {
var vertices:Vector.<Vertex> = new Vector.<Vertex>();
var faces:Vector.<Face> = new Vector.<Face>();
var numVertices:int = 0;
var numFaces:int = 0;
var n:int;
var m:int;
var face:Face;
var vertex:Vertex;
var correct:Boolean = false;
if (animationData != null) {
var a:Number = objectData.a;
var b:Number = objectData.b;
var c:Number = objectData.c;
var d:Number = objectData.d;
var e:Number = objectData.e;
var f:Number = objectData.f;
var g:Number = objectData.g;
var h:Number = objectData.h;
var i:Number = objectData.i;
var j:Number = objectData.j;
var k:Number = objectData.k;
var l:Number = objectData.l;
var det:Number = 1/(-c*f*i + b*g*i + c*e*j - a*g*j - b*e*k + a*f*k);
objectData.a = (-g*j + f*k)*det;
objectData.b = (c*j - b*k)*det;
objectData.c = (-c*f + b*g)*det;
objectData.d = (d*g*j - c*h*j - d*f*k + b*h*k + c*f*l - b*g*l)*det;
objectData.e = (g*i - e*k)*det;
objectData.f = (-c*i + a*k)*det;
objectData.g = (c*e - a*g)*det;
objectData.h = (c*h*i - d*g*i + d*e*k - a*h*k - c*e*l + a*g*l)*det;
objectData.i = (-f*i + e*j)*det;
objectData.j = (b*i - a*j)*det;
objectData.k = (-b*e + a*f)*det;
objectData.l = (d*f*i - b*h*i - d*e*j + a*h*j + b*e*l - a*f*l)*det;
if (animationData.pivot != null) {
objectData.d -= animationData.pivot.x;
objectData.h -= animationData.pivot.y;
objectData.l -= animationData.pivot.z;
}
correct = true;
}
// Создание и корректировка вершин
for (n = 0,m = 0; n < objectData.vertices.length;) {
vertex = new Vertex();
if (correct) {
var x:Number = objectData.vertices[n++];
var y:Number = objectData.vertices[n++];
var z:Number = objectData.vertices[n++];
vertex.x = objectData.a*x + objectData.b*y + objectData.c*z + objectData.d;
vertex.y = objectData.e*x + objectData.f*y + objectData.g*z + objectData.h;
vertex.z = objectData.i*x + objectData.j*y + objectData.k*z + objectData.l;
} else {
vertex.x = objectData.vertices[n++];
vertex.y = objectData.vertices[n++];
vertex.z = objectData.vertices[n++];
}
vertex.x *= scale;
vertex.y *= scale;
vertex.z *= scale;
vertex.u = objectData.uvs[m++];
vertex.v = 1 - objectData.uvs[m++];
vertex.transformID = -1;
vertices[numVertices++] = vertex;
vertex.next = mesh.vertexList;
mesh.vertexList = vertex;
}
// Создание граней
var last:Face;
for (n = 0; n < objectData.faces.length;) {
face = new Face();
face.wrapper = new Wrapper();
face.wrapper.next = new Wrapper();
face.wrapper.next.next = new Wrapper();
face.wrapper.vertex = vertices[objectData.faces[n++]];
face.wrapper.next.vertex = vertices[objectData.faces[n++]];
face.wrapper.next.next.vertex = vertices[objectData.faces[n++]];
faces[numFaces++] = face;
if (last != null) {
last.next = face;
} else {
mesh.faceList = face;
}
last = face;
}
// Назначение материалов
if (objectData.surfaces != null) {
for (var key:String in objectData.surfaces) {
var surface:Vector.<int> = objectData.surfaces[key];
var materialData:MaterialData = materialDatas[key];
var material:Material = materialData.material;
for (n = 0; n < surface.length; n++) {
face = faces[surface[n]];
face.material = material;
// Коррекция UV-координат
if (materialData.matrix != null) {
for (var w:Wrapper = face.wrapper; w != null; w = w.next) {
vertex = w.vertex;
if (vertex.transformID < 0) {
var u:Number = vertex.u;
var v:Number = vertex.v;
vertex.u = materialData.matrix.a*u + materialData.matrix.b*v + materialData.matrix.tx;
vertex.v = materialData.matrix.c*u + materialData.matrix.d*v + materialData.matrix.ty;
vertex.transformID = 0;
}
}
}
}
}
}
// Назначение материала по-умолчанию для граней без поверхностей
var defaultMaterial:FillMaterial = new FillMaterial(0x7F7F7F);
defaultMaterial.name = "default";
for (face = mesh.faceList; face != null; face = face.next) {
if (face.material == null) {
face.material = defaultMaterial;
}
}
// Расчёт нормалей
mesh.calculateNormals(true);
mesh.calculateBounds();
}
private function getString(index:int):String {
data.position = index;
var charCode:int;
var res:String = "";
while ((charCode = data.readByte()) != 0) {
res += String.fromCharCode(charCode);
}
return res;
}
private function getRotationFrom3DSAngleAxis(angle:Number, x:Number, z:Number, y:Number):Vector3D {
var res:Vector3D = new Vector3D();
var s:Number = Math.sin(angle);
var c:Number = Math.cos(angle);
var t:Number = 1 - c;
var k:Number = x*y*t + z*s;
var half:Number;
if (k >= 1) {
half = angle/2;
res.z = -2*Math.atan2(x*Math.sin(half), Math.cos(half));
res.y = -Math.PI/2;
res.x = 0;
return res;
}
if (k <= -1) {
half = angle/2;
res.z = 2*Math.atan2(x*Math.sin(half), Math.cos(half));
res.y = Math.PI/2;
res.x = 0;
return res;
}
res.z = -Math.atan2(y*s - x*z*t, 1 - (y*y + z*z)*t);
res.y = -Math.asin(x*y*t + z*s);
res.x = -Math.atan2(x*s - y*z*t, 1 - (x*x + z*z)*t);
return res;
}
}
}
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.materials.Material;
import flash.geom.Matrix;
import flash.geom.Vector3D;
class MaterialData {
public var name:String;
public var color:int;
public var specular:int;
public var glossiness:int;
public var transparency:int;
public var diffuseMap:MapData;
public var opacityMap:MapData;
public var matrix:Matrix;
public var material:Material;
}
class MapData {
public var filename:String;
public var scaleU:Number = 1;
public var scaleV:Number = 1;
public var offsetU:Number = 0;
public var offsetV:Number = 0;
public var rotation:Number = 0;
}
class ObjectData {
public var name:String;
public var vertices:Vector.<Number>;
public var uvs:Vector.<Number>;
public var faces:Vector.<int>;
public var surfaces:Object;
public var a:Number;
public var b:Number;
public var c:Number;
public var d:Number;
public var e:Number;
public var f:Number;
public var g:Number;
public var h:Number;
public var i:Number;
public var j:Number;
public var k:Number;
public var l:Number;
}
class AnimationData {
public var objectName:String;
public var object:Object3D;
public var parentIndex:int;
public var pivot:Vector3D;
public var position:Vector3D;
public var rotation:Vector3D;
public var scale:Vector3D;
public var isInstance:Boolean;
}
class ChunkInfo {
public var id:int;
public var size:int;
public var dataSize:int;
public var dataPosition:int;
public var nextChunkPosition:int;
}

View File

@@ -0,0 +1,582 @@
package alternativa.engine3d.loaders {
import alternativa.engine3d.animation.Animation;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Object3DContainer;
import alternativa.engine3d.loaders.collada.DaeAnimatedObject;
import alternativa.engine3d.loaders.collada.DaeDocument;
import alternativa.engine3d.loaders.collada.DaeMaterial;
import alternativa.engine3d.loaders.collada.DaeNode;
import alternativa.engine3d.materials.Material;
import alternativa.engine3d.materials.TextureMaterial;
import flash.geom.Matrix3D;
/**
* Класс выполняет парсинг xml коллады.
*/
public class ParserCollada {
/**
* Список объектов.
*/
public var objects:Vector.<Object3D>;
/**
* Список родителей объектов. Количество и порядок расположения элементов соответствует массиву objects.
*
* @see #objects
*/
public var parents:Vector.<Object3D>;
/**
* Список корневых (без родителей) объектов.
*/
public var hierarchy:Vector.<Object3D>;
/**
* Список камер.
*/
public var cameras:Vector.<Camera3D>;
/**
* Список всех материалов.
*
* @see #textureMaterials
*/
public var materials:Vector.<Material>;
/**
* Список текстурных материалов.
* Можно использовать класс MaterialLoader для загрузки текстур этих материалов.
*
* @see #materials
* @see MaterialLoader
*/
public var textureMaterials:Vector.<TextureMaterial>;
/**
* Массив анимаций.
*/
public var animations:Vector.<Animation>;
/**
* Создает экземпляр парсера
*/
public function ParserCollada() {
}
/**
* Зачищает все ссылки на внешние объекты.
*/
public function clean():void {
objects = null;
parents = null;
hierarchy = null;
cameras = null;
animations = null;
materials = null;
textureMaterials = null;
}
/**
* Инициализация перед парсингом.
*/
private function init(data:XML):DaeDocument {
clean();
objects = new Vector.<Object3D>();
parents = new Vector.<Object3D>();
hierarchy = new Vector.<Object3D>();
cameras = new Vector.<Camera3D>();
animations = new Vector.<Animation>();
materials = new Vector.<Material>();
textureMaterials = new Vector.<TextureMaterial>();
return new DaeDocument(data);
}
/**
* Метод распарсивает xml коллады и заполняет массивы objects, parents, hierarchy, cameras, materials, textureMaterials, animations.
* Для загрузки текстур сцены можно использовать класс MaterialLoader.
*
* @param data xml содержимое коллады
* @param baseURL адрес относительно которого выполняется поиск текстур. Путь к файлу коллады.
* Должен соответствовать спецификации URI. Например file:///C:/test.dae или /C:/test.dae для полных путей или test.dae, ./test.dae для относительных.
* @param skipEmptyObjects при значении <code>true</code> объекты без содержимого не будут создаваться без необходимости.
*
* @example Пример загрузки файла коллады, парсинга, загрузки текстур и создания иерархии:
* <listing version="3.0">package {
*
* import alternativa.engine3d.containers.AverageZContainer;
* import alternativa.engine3d.core.Object3D;
* import alternativa.engine3d.core.Object3DContainer;
* import alternativa.engine3d.loaders.MaterialLoader;
* import alternativa.engine3d.loaders.ParserCollada;
*
* import flash.display.Sprite;
* import flash.events.Event;
* import flash.geom.Matrix3D;
* import flash.net.URLLoader;
* import flash.net.URLRequest;
*
* public class ColladaExample extends Sprite {
*
* private const modelURL:String = "model.dae";
*
* private var loader:URLLoader;
* private var materialLoader:MaterialLoader;
*
* public function ColladaExample() {
* loader = new URLLoader();
* loader.addEventListener(Event.COMPLETE, onModelLoad);
* loader.load(new URLRequest(modelURL));
* }
*
* private function onModelLoad(e:Event):void {
* var collada:ParserCollada = new ParserCollada();
* collada.parse(XML(loader.data), modelURL);
*
* var container:AverageZContainer = new AverageZContainer();
* // Создаем иерархию объектов
* var objects:Vector.<Object3D> = collada.hierarchy;
* for (var o:int = 0, count:int = objects.length; o < count; o++) {
* container.addChild(objects[o]);
* }
*
* // Начинаем загрузку текстур материалов
* materialLoader = new MaterialLoader();
* materialLoader.addEventListener(Event.COMPLETE, onMaterialsLoad);
* materialLoader.load(collada.textureMaterials);
* }
*
* private function onMaterialsLoad(e:Event):void {
* trace("Loading complete");
* }
*
* }}</listing>
*
* @see MaterialLoader
* @see #objects
* @see #parents
* @see #hierarchy
* @see #cameras
* @see #materials
* @see #textureMaterials
*/
public function parse(data:XML, baseURL:String = null, skipEmptyObjects:Boolean = true):void {
var document:DaeDocument = init(data);
if (document.scene != null) {
parseNodes(document.scene.nodes, null, skipEmptyObjects);
parseMaterials(document.materials, baseURL);
}
}
/**
* Метод распарсивает xml коллады и заполняет массивы objects, parents, hierarchy, cameras, materials, textureMaterials, animations.
* Для загрузки текстур сцены можно использовать класс MaterialLoader.
* После парсинга для объектов, которые содержат детей, но не являются контейнерами, будут созданы контейнеры.
*
* @param data xml содержимое коллады
* @param baseURL адрес относительно которого выполняется поиск текстур. Путь к файлу коллады.
* Должен соответствовать спецификации URI. Например file:///C:/test.dae или /C:/test.dae для полных путей или test.dae, ./test.dae для относительных.
* @param skipEmptyObjects при значении <code>true</code> объекты без содержимого не будут создаваться без необходимости.
*
* @see MaterialLoader
* @see #objects
* @see #parents
* @see #hierarchy
* @see #cameras
* @see #materials
* @see #textureMaterials
*/
public function parseForAnimation(data:XML, baseURL:String = null, skipEmptyObjects:Boolean = true):void {
var document:DaeDocument = init(data);
if (document.scene != null) {
parseNodesForAnimation(document.scene.nodes, null, skipEmptyObjects);
parseMaterials(document.materials, baseURL);
}
}
/**
* Метод распарсивает xml коллады и заполняет массивы objects, parents, hierarchy, cameras, materials, textureMaterials, animations.
* Для загрузки текстур сцены можно использовать класс MaterialLoader.
* После парсинга объекты, у которых родитель не является контейнером, будут переведены в систему координат ближайшего родительского контейнера.
*
* @param data xml содержимое коллады
* @param baseURL адрес относительно которого выполняется поиск текстур. Путь к файлу коллады.
* Должен соответствовать спецификации URI. Например file:///C:/test.dae или /C:/test.dae для полных путей или test.dae, ./test.dae для относительных.
* @param skipEmptyObjects при значении <code>true</code> объекты без содержимого не будут создаваться без необходимости.
*
* @see MaterialLoader
* @see #objects
* @see #parents
* @see #hierarchy
* @see #cameras
* @see #materials
* @see #textureMaterials
*/
public function parseForStatic(data:XML, baseURL:String = null, skipEmptyObjects:Boolean = true):void {
var document:DaeDocument = init(data);
if (document.scene != null) {
parseNodesForStatic(document.scene.nodes, null, null, skipEmptyObjects);
parseMaterials(document.materials, baseURL);
}
}
/**
* Проверяет есть ли в иерархии объекты с заданными параметрами
*
* @param skipEmptyObjects Если установлен в <code>true</code>, будет пропускать ноды без объектов
* @param skinsOnly Если установлен в <code>true</code>, будут пропускаться все ноды кроме тех, что содержат скин.
*/
private function hasSignifiantChildren(node:DaeNode, skipEmptyObjects:Boolean, skinsOnly:Boolean):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;
} else {
if (!skinsOnly && !node.skinOrRootJoint && (!skipEmptyObjects || child.objects != null)) {
return true;
}
}
if (hasSignifiantChildren(child, skipEmptyObjects, skinsOnly)) {
return true;
}
}
return false;
}
/**
* Добавляет компоненты анимированного объекта в списки objects, parents, hierarchy, cameras, animations и в родительский контейнер.
*/
private function addObject(animatedObject:DaeAnimatedObject, parent:Object3D):Object3D {
var object:Object3D = animatedObject.object;
this.objects.push(object);
this.parents.push(parent);
if (parent == null) {
this.hierarchy.push(object);
}
var container:Object3DContainer = parent as Object3DContainer;
if (container != null) {
container.addChild(object);
}
if (object is Camera3D) {
this.cameras.push(object as Camera3D);
}
if (animatedObject.animation != null) {
this.animations.push(animatedObject.animation);
}
return object;
}
/**
* Добавляет объекты в списки objects, parents, hierarchy, cameras, animations и в родительский контейнер.
*
* @return первый объект
*/
private function addObjects(animatedObjects:Vector.<DaeAnimatedObject>, parent:Object3D):Object3D {
var first:Object3D = addObject(animatedObjects[0], parent);
for (var i:int = 1, count:int = animatedObjects.length; i < count; i++) {
addObject(animatedObjects[i], parent);
}
return first;
}
private function parseNodes(nodes:Vector.<DaeNode>, parent:Object3D, skipEmptyObjects:Boolean, skinsOnly:Boolean = false):void {
for (var i:int = 0, count:int = nodes.length; i < count; i++) {
var node:DaeNode = nodes[i];
node.parse();
var object:Object3D;
if (node.skins != null) {
object = addObjects(node.skins, parent);
} else {
if (!node.skinOrRootJoint && !skinsOnly) {
if (node.objects != null) {
object = addObjects(node.objects, parent);
} else {
if (!skipEmptyObjects) {
object = new Object3D();
object.name = node.name;
object = addObject(node.applyAnimation(node.applyTransformations(object)), parent);
}
}
}
}
// Если это кость или скин, парсим только скины у детей
skinsOnly = skinsOnly || node.skinOrRootJoint;
if (object == null) {
if (hasSignifiantChildren(node, skipEmptyObjects, skinsOnly)) {
object = new Object3D();
object.name = node.name;
parseNodes(node.nodes, addObject(node.applyAnimation(node.applyTransformations(object)), parent), skipEmptyObjects, skinsOnly);
}
} else {
parseNodes(node.nodes, object, skipEmptyObjects, skinsOnly);
}
}
}
private function parseNodesForAnimation(nodes:Vector.<DaeNode>, parent:Object3DContainer, skipEmptyObjects:Boolean, skinsOnly:Boolean = false):void {
for (var i:int = 0, count:int = nodes.length; i < count; i++) {
var node:DaeNode = nodes[i];
node.parse();
var container:Object3DContainer = null;
if (node.skins != null) {
// Основная кость скина
addObjects(node.skins, parent);
} else {
if (!node.skinOrRootJoint && !skinsOnly) {
if (node.objects != null) {
container = addObjects(node.objects, parent) as Object3DContainer;
} else {
// Нет объектов в ноде
if (!skipEmptyObjects) {
var object:Object3D = new Object3D();
object.name = node.name;
addObject(node.applyAnimation(node.applyTransformations(object)), parent);
}
}
}
}
// Парсим детей
// Если это кость или скин, парсим только скины у детей
skinsOnly = skinsOnly || node.skinOrRootJoint;
if (container == null) {
if (hasSignifiantChildren(node, skipEmptyObjects, skinsOnly)) {
container = new Object3DContainer();
if (node.name != null) {
container.name = node.name + "-container";
}
addObject(node.applyAnimation(node.applyTransformations(container)), parent);
parseNodesForAnimation(node.nodes, container, skipEmptyObjects, skinsOnly);
}
} else {
parseNodesForAnimation(node.nodes, container, skipEmptyObjects, skinsOnly);
}
}
}
private function appendMatrixToObjects(objects:Vector.<DaeAnimatedObject>, append:Matrix3D):void {
for (var i:int = 0, count:int = objects.length; i < count; i++) {
var object:Object3D = objects[i].object;
var matrix:Matrix3D = object.getMatrix();
matrix.append(append);
object.setMatrix(matrix);
}
}
private function parseNodesForStatic(nodes:Vector.<DaeNode>, parent:Object3DContainer, toParentMatrix:Matrix3D, skipEmptyObjects:Boolean, skinsOnly:Boolean = false):void {
for (var i:int = 0, count:int = nodes.length; i < count; i++) {
var node:DaeNode = nodes[i];
node.parse();
var container:Object3DContainer = null;
if (node.skins != null) {
if (toParentMatrix != null) {
appendMatrixToObjects(node.skins, toParentMatrix);
}
// Основная кость скина
addObjects(node.skins, parent);
} else {
if (!node.skinOrRootJoint && !skinsOnly) {
if (node.objects != null) {
if (toParentMatrix != null) {
appendMatrixToObjects(node.skins, toParentMatrix);
}
container = addObjects(node.objects, parent) as Object3DContainer;
} else {
// Нет объектов в ноде
if (!skipEmptyObjects) {
var object:Object3D = new Object3DContainer();
object.name = node.name;
addObject(node.applyAnimation(node.applyTransformations(object, null, toParentMatrix)), parent);
}
}
}
}
// Парсим детей
// Если это кость или скин, парсим только скины у детей
skinsOnly = skinsOnly || node.skinOrRootJoint;
if (container == null) {
if (toParentMatrix == null) {
toParentMatrix = node.getMatrix();
} else {
toParentMatrix.append(node.getMatrix());
}
parseNodesForStatic(node.nodes, parent, toParentMatrix, skipEmptyObjects, skinsOnly);
} else {
parseNodesForStatic(node.nodes, container, null, skipEmptyObjects, skinsOnly);
}
}
}
private function parseMaterials(materials:Object, baseURL:String):void {
var tmaterial:TextureMaterial;
for each (var material:DaeMaterial in materials) {
if (material.used) {
material.parse();
this.materials.push(material.material);
if (material.material is TextureMaterial) {
tmaterial = material.material as TextureMaterial;
if (tmaterial.texture == null) {
// Филлы тоже задаются текстурным материалом на данный момент
textureMaterials.push(tmaterial);
}
}
}
}
if (baseURL != null) {
baseURL = fixURL(baseURL);
var end:int = baseURL.lastIndexOf("/");
var base:String = (end < 0) ? "" : baseURL.substring(0, end + 1);
for each (tmaterial in textureMaterials) {
if (tmaterial.diffuseMapURL != null) {
tmaterial.diffuseMapURL = resolveURL(fixURL(tmaterial.diffuseMapURL), base);
}
if (tmaterial.opacityMapURL != null) {
tmaterial.opacityMapURL = resolveURL(fixURL(tmaterial.opacityMapURL), base);
}
}
} else {
for each (tmaterial in textureMaterials) {
if (tmaterial.diffuseMapURL != null) {
tmaterial.diffuseMapURL = fixURL(tmaterial.diffuseMapURL);
}
if (tmaterial.opacityMapURL != null) {
tmaterial.opacityMapURL = fixURL(tmaterial.opacityMapURL);
}
}
}
}
/**
* @private
* Приводит урл к следующему виду:
* Обратные слеши в пути заменяет на прямые
* Три прямых слеша после схемы file:
*/
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) : "");
}
// public function resolveTest(url:String, base:String):void {
// trace('was::"' + base + '" "' + url + '"');
// trace('now::"' + resolveURL(fixURL(url), fixURL(base)) + '"');
// }
/**
* @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
* Конвертирует относительные пути в полные
*/
private function resolveURL(url:String, base:String):String {
// http://labs.apache.org/webarch/uri/rfc/rfc3986.html
if (url.charAt(0) == "." && url.charAt(1) == "/") {
// Файл в той же папке
return base + url.substring(2);
} else if (url.charAt(0) == "/") {
// Полный путь
return url;
} else if (url.charAt(0) == "." && url.charAt(1) == ".") {
// Выше по уровню
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);
}
// Делим базовый урл на составные части
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) {
// Присутствует схема, нет домена
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 {
// Нет схемы, нет домена
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)) {
// Содержит схему
return url;
}
return base + "/" + url;
}
public function getObjectByName(name:String):Object3D {
for (var i:int = 0, count:int = objects.length; i < count; i++) {
var object:Object3D = objects[i];
if (object.name == name) {
return object;
}
}
return null;
}
}
}

View File

@@ -0,0 +1,235 @@
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.containers.ConflictContainer;
import alternativa.engine3d.containers.KDTree;
import alternativa.engine3d.containers.ZSortContainer;
import alternativa.engine3d.core.Clipping;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Object3DContainer;
import alternativa.engine3d.core.Sorting;
import alternativa.engine3d.materials.Material;
import alternativa.engine3d.objects.LOD;
import alternativa.engine3d.objects.Mesh;
import alternativa.engine3d.objects.Skin;
import alternativa.engine3d.objects.Sprite3D;
import flash.utils.getDefinitionByName;
/**
* @private
*/
public class DaeAlternativa3DObject extends DaeElement {
use namespace collada;
use namespace alternativa3d;
public function DaeAlternativa3DObject(data:XML, document:DaeDocument) {
super(data, document);
}
private function createObject(className:String):* {
try {
var ClassDef:Class = getDefinitionByName(className) as Class;
return new ClassDef();
} catch (e:ReferenceError) {
trace("[ERROR]", e.message);
}
return null;
}
public function parseContainer():Object3DContainer {
var container:Object3DContainer;
var classNameXML:XML = data.@className[0];
switch (data.localName()) {
case "object3d": {
if (classNameXML != null) {
container = createObject(classNameXML.toString());
} else {
container = new Object3DContainer();
}
return setParams(container);
}
case "averageZ": {
if (classNameXML != null) {
container = createObject(classNameXML.toString());
} else {
container = new ZSortContainer();
}
return setParams(container);
}
case "conflict": {
if (classNameXML != null) {
container = createObject(classNameXML.toString());
} else {
container = new ConflictContainer();
}
return setParams(container);
}
case "kdTree": {
if (classNameXML != null) {
container = createObject(classNameXML.toString());
} else {
container = new KDTree();
}
return setParams(container);
}
}
return null;
}
private function getClippingValue(clipping:XML):int {
switch (clipping.toString()) {
case "BOUND_CULLING":
return Clipping.BOUND_CULLING;
break;
case "FACE_CULLING":
return Clipping.FACE_CULLING;
break;
case "FACE_CLIPPING":
return Clipping.FACE_CLIPPING;
break;
}
return Clipping.BOUND_CULLING;
}
private function getSortingValue(sorting:XML):int {
switch (sorting.toString()) {
case "STATIC_BSP":
return Sorting.STATIC_BSP;
break;
case "DYNAMIC_BSP":
return Sorting.DYNAMIC_BSP;
break;
case "NONE":
return Sorting.NONE;
break;
case "AVERAGE_Z":
return Sorting.AVERAGE_Z;
break;
}
return Sorting.NONE;
}
public function parseSprite3D(material:Material = null):Sprite3D {
if (data.localName() == "sprite") {
var sprite:Sprite3D;
var classNameXML:XML = data.@className[0];
if (classNameXML != null) {
sprite = createObject(classNameXML.toString());
} else {
sprite = new Sprite3D();
}
sprite.material = material;
var sortingXML:XML = data.sorting[0];
var clippingXML:XML = data.clipping[0];
if (sortingXML != null) {
sprite.sorting = getSortingValue(sortingXML);
}
if (clippingXML != null) {
sprite.clipping = getClippingValue(clippingXML);
}
return setParams(sprite);
}
return null;
}
public function parseMesh(skin:Boolean = false):Mesh {
if (data.localName() == "mesh") {
var mesh:Mesh;
var classNameXML:XML = data.@className[0];
if (classNameXML != null) {
mesh = createObject(classNameXML.toString());
} else {
mesh = (skin) ? new Skin() : new Mesh();
}
var sortingXML:XML = data.sorting[0];
var clippingXML:XML = data.clipping[0];
var optimizeXML:XML = data.optimizeBSP[0];
if (clippingXML != null) {
mesh.clipping = getClippingValue(clippingXML);
}
var optimize:Boolean = (optimizeXML != null) ? optimizeXML.toString() != "false" : true;
if (sortingXML != null) {
mesh.sorting = getSortingValue(sortingXML);
if (mesh.sorting == Sorting.STATIC_BSP) {
mesh.calculateBSP(optimize);
} else if (mesh.sorting == Sorting.DYNAMIC_BSP && optimize) {
mesh.optimizeForDynamicBSP();
}
}
return setParams(mesh);
}
return null;
}
public function parseLOD():LOD {
if (data.localName() == "LOD" || data.localName() == "lod") {
var lod:LOD;
var classNameXML:XML = data.@className[0];
if (classNameXML != null) {
lod = createObject(classNameXML.toString());
} else {
lod = new LOD();
}
var levels:XMLList = data.level;
var count:int = levels.length();
var distances:Vector.<Number> = lod.lodDistances = new Vector.<Number>(count);
var objects:Vector.<Object3D> = lod.lodObjects = new Vector.<Object3D>(count);
for (var i:int = 0; i < count; i++) {
var level:XML = levels[i];
distances[i] = parseNumber(level.@distance[0]);
var node:DaeNode = document.findNode(level.@url[0]);
if (node != null) {
if (node.rootJoint != null) {
node = node.rootJoint;
node.parse();
if (node.skins.length > 0) {
objects[i] = node.skins[0].object;
}
} else {
if (node.objects.length > 0) {
objects[i] = node.objects[0].object;
}
}
} else {
document.logger.logNotFoundError(level.@url[0]);
}
}
return setParams(lod);
}
return new LOD();
}
private function setParams(object:*):* {
var params:XMLList = data.param;
for (var i:int = 0, count:int = params.length(); i < count; i++) {
var param:XML = params[i];
try {
var name:String = param.@name[0].toString();
var value:String = param.text().toString();
if (value == "true") {
object[name] = true;
} else if (value == "false") {
object[name] = false;
} else if ((value.charAt(0) == '"' && value.charAt(value.length - 1) == '"') || (value.charAt(0) == "'" && value.charAt(value.length - 1) == "'")) {
object[name] = value;
} else {
if (value.indexOf(".") >= 0) {
object[name] = parseFloat(value);
} else if (value.indexOf(",") >= 0) {
value = value.replace(/,/, ".");
object[name] = parseFloat(value);
} else {
object[name] = parseInt(value);
}
}
} catch (e:Error) {
trace("[ERROR]", e.message);
}
}
return object;
}
}
}

View File

@@ -0,0 +1,20 @@
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.animation.Animation;
import alternativa.engine3d.core.Object3D;
/**
* @private
*/
public class DaeAnimatedObject {
public var object:Object3D;
public var animation:Animation;
public function DaeAnimatedObject(object:Object3D, animation:Animation = null) {
this.object = object;
this.animation = animation;
}
}
}

View File

@@ -0,0 +1,41 @@
package alternativa.engine3d.loaders.collada {
/**
* @private
*/
public class DaeArray extends DaeElement {
use namespace collada;
/**
* Массив значений типа String.
* Перед использованием вызвать parse().
*/
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,55 @@
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.core.Camera3D;
/**
* @private
*/
public class DaeCamera extends DaeElement {
use namespace collada;
public function DaeCamera(data:XML, document:DaeDocument) {
super(data, document);
}
private function setXFov(camera:Camera3D, xFov:Number):void {
//camera.fov = 2*Math.atan(0.5*Math.sqrt(camera.width*camera.width + camera.height*camera.height)/(camera.width/(2*Math.tan(0.5*xFov))));
}
public function parseCamera():Camera3D {
var camera:Camera3D = new Camera3D();
var perspectiveXML:XML = data.optics.technique_common.perspective[0];
if (perspectiveXML) {
const DEG2RAD:Number = Math.PI/180;
var xfovXML:XML = perspectiveXML.xfov[0];
var yfovXML:XML = perspectiveXML.yfov[0];
var ratioXML:XML = perspectiveXML.aspect_ratio[0];
if (ratioXML == null) {
if (xfovXML != null) {
setXFov(camera, parseNumber(xfovXML)*DEG2RAD);
} else if (yfovXML != null) {
setXFov(camera, parseNumber(yfovXML)*DEG2RAD);
}
} else {
var ratio:Number = parseNumber(ratioXML);
//camera.height = camera.width/ratio;
if (xfovXML != null) {
setXFov(camera, parseNumber(xfovXML)*DEG2RAD);
} else if (yfovXML != null) {
setXFov(camera, ratio*parseNumber(yfovXML)*DEG2RAD);
}
}
var znearXML:XML = perspectiveXML.znear[0];
var zfarXML:XML = perspectiveXML.zfar[0];
if (znearXML != null) {
camera.nearClipping = parseNumber(znearXML);
}
if (zfarXML != null) {
camera.farClipping = parseNumber(zfarXML);
}
}
return camera;
}
}
}

View File

@@ -0,0 +1,185 @@
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.animation.Key;
import alternativa.engine3d.animation.Track;
import alternativa.engine3d.animation.ValueKey;
/**
* @private
*/
public class DaeChannel extends DaeElement {
static public const PARAM_UNDEFINED:int = -1;
static public const PARAM_TRANSLATE_X:int = 0;
static public const PARAM_TRANSLATE_Y:int = 1;
static public const PARAM_TRANSLATE_Z:int = 2;
static public const PARAM_SCALE_X:int = 3;
static public const PARAM_SCALE_Y:int = 4;
static public const PARAM_SCALE_Z:int = 5;
static public const PARAM_ROTATION_X:int = 6;
static public const PARAM_ROTATION_Y:int = 7;
static public const PARAM_ROTATION_Z:int = 8;
static public const PARAM_TRANSLATE:int = 9;
static public const PARAM_SCALE:int = 10;
static public const PARAM_MATRIX:int = 11;
/**
* Анимационный трек с ключами.
* Перед использованием вызвать parse().
*/
public var track:Track;
/**
* Тип анимированного параметра, принимает одно из значений DaeChannel.PARAM_*.
* Перед использованием вызвать parse().
*/
public var animatedParam:int = PARAM_UNDEFINED;
public function DaeChannel(data:XML, document:DaeDocument) {
super(data, document);
}
/**
* Возвращает ноду которой предназначена анимация.
*/
public function get node():DaeNode {
var targetXML:XML = data.@target[0];
if (targetXML != null) {
var targetParts:Array = targetXML.toString().split("/");
// Первая часть это id элемента
var node:DaeNode = document.findNodeByID(targetParts[0]);
if (node != null) {
// Последняя часть это трансформируемый элемент
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;
// Разбиваем путь на части
var targetParts:Array = targetXML.toString().split("/");
var sid:String = targetParts.pop();
var sidParts:Array = sid.split(".");
var sidPartsCount:int = sidParts.length;
// Определяем тип свойства
var transformationXML:XML;
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:: вариант со скобками на всякий случай
var transformationName:String = transformationXML.localName() as String;
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:: искать максимальное значение, а не единицу
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) {
track = sampler.parseMatrixTrack();
return;
}
if (animatedParam == PARAM_TRANSLATE || animatedParam == PARAM_SCALE) {
track = sampler.parsePointsTrack();
return;
}
track = sampler.parseValuesTrack();
if (animatedParam == PARAM_ROTATION_X || animatedParam == PARAM_ROTATION_Y || animatedParam == PARAM_ROTATION_Z) {
// Переводим углы в радианы
var toRad:Number = Math.PI/180;
for (var key:Key = track.keyList; key != null; key = key.next) {
var valueKey:ValueKey = ValueKey(key);
valueKey.value *= toRad;
}
}
} else {
document.logger.logNotFoundError(data.@source[0]);
}
}
}
}

View File

@@ -0,0 +1,471 @@
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.*;
import alternativa.engine3d.animation.Animation;
import alternativa.engine3d.animation.ComplexAnimation;
import alternativa.engine3d.animation.ObjectAnimation;
import alternativa.engine3d.core.Vertex;
import alternativa.engine3d.objects.Joint;
import alternativa.engine3d.objects.Skin;
import flash.geom.Matrix3D;
import flash.utils.Dictionary;
/**
* @private
*/
public class DaeController extends DaeElement {
use namespace collada;
use namespace alternativa3d;
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;
public function DaeController(data:XML, document:DaeDocument) {
super(data, document);
// sources мы создаем внутри DaeDocument, здесь не нужно.
}
override protected function parseImplementation():Boolean {
var vertexWeightsXML:XML = 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();
return true;
}
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;
}
/**
* Парсит инверсные матрицы для костей и сохраняет их в вектор.
*/
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]);
}
}
}
private function get geometry():DaeGeometry {
var geom:DaeGeometry = document.findGeometry(data.skin.@source[0]);
if (geom == null) {
document.logger.logNotFoundError(data.@source[0]);
}
return geom;
}
/**
* Возвращает геометрию с костями и контроллер для костей.
* Перед использованием вызвать parse().
*/
public function parseSkin(skinNode:DaeNode, materials:Object, skeletons:Vector.<DaeNode>):DaeAnimatedObject {
var skinXML:XML = data.skin[0];
if (skinXML != null) {
var skin:Skin;
var geom:DaeGeometry = geometry;
if (geom != null) {
geom.parse();
skin = geom.parseAlternativa3DObject(true) as Skin;
if (skin == null) {
skin = new Skin();
}
var vertices:Vector.<Vertex> = geom.fillInMesh(skin, materials);
applyBindShapeMatrix(skin);
var joints:Vector.<DaeAnimatedObject> = addJointsToSkin(skin, skinNode, findNodes(skeletons));
setJointsBindMatrices(joints);
linkVerticesToJoints(joints, vertices);
skin.normalizeWeights();
geom.cleanVertices(skin);
skin.calculateNormals(true);
skin.calculateBounds();
return new DaeAnimatedObject(skin, mergeJointsAnimations(joints));
} else {
skin = new Skin();
skin.calculateNormals(true);
skin.calculateBounds();
return new DaeAnimatedObject(skin);
}
}
return null;
}
/**
* Объединяет анимацию костей в одну анимацию, если требуется
*/
private function mergeJointsAnimations(joints:Vector.<DaeAnimatedObject>):Animation {
var animation:Animation;
var complexAnimation:ComplexAnimation;
for (var i:int = 0, count:int = joints.length; i < count; i++) {
var animatedObject:DaeAnimatedObject = joints[i];
if (animatedObject.animation != null) {
if (animation == null) {
// Первая анимация
animation = animatedObject.animation;
} else {
if (complexAnimation == null) {
// Вторая анимация, создаем комплексную и добавляем в нее первую анимацию
complexAnimation = new ComplexAnimation();
complexAnimation.addAnimation(animation);
animation = complexAnimation;
}
complexAnimation.addAnimation(animatedObject.animation);
}
}
}
return animation;
}
/**
* Задает костям их инверсные матрицы.
*/
private function setJointsBindMatrices(animatedJoints:Vector.<DaeAnimatedObject>):void {
for (var i:int = 0, count:int = jointsBindMatrices.length; i < count; i++) {
var animatedJoint:DaeAnimatedObject = animatedJoints[i];
var bindMatrix:Vector.<Number> = jointsBindMatrices[i];
Joint(animatedJoint.object).setBindingMatrix(bindMatrix[0], bindMatrix[1], bindMatrix[2], bindMatrix[3],
bindMatrix[4], bindMatrix[5], bindMatrix[6], bindMatrix[7],
bindMatrix[8], bindMatrix[9], bindMatrix[10], bindMatrix[11]);
}
}
/**
* Связывает вершину и все ее дубликаты с костью
*/
private function linkVertexToJoint(joint:Joint, vertex:Vertex, weight:Number):void {
joint.bindVertex(vertex, weight);
// Цепляем дубликаты
while ((vertex = vertex.value) != null) {
joint.bindVertex(vertex, weight);
}
}
/**
* Связывает вершины с костями
*/
private function linkVerticesToJoints(animatedJoints:Vector.<DaeAnimatedObject>, vertices:Vector.<Vertex>):void {
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 vertexIndex:int = 0;
for (var i:int = 0, numVertices:int = vertices.length; i < numVertices; i++) {
var vertex:Vertex = vertices[i];
var count:int = vcounts[i];
for (var j:int = 0; j < count; j++) {
var index:int = inputsStride*(vertexIndex + j);
var jointIndex:int = indices[int(index + jointsOffset)];
if (jointIndex >= 0) {
var weightIndex:int = indices[int(index + weightsOffset)];
var weight:Number = weights[int(weightsStride*weightIndex)];
linkVertexToJoint(Joint(animatedJoints[jointIndex].object), vertex, weight);
}
}
vertexIndex += count;
}
}
/**
* Создает иерархию костей и добавляет к скину.
*
* @return вектор добавленых к скину костей с анимацией.
* Если были добавлены вспомогательные кости, длина вектора будет отличаться от длины вектора nodes
*/
private function addJointsToSkin(skin:Skin, skinNode:DaeNode, nodes:Vector.<DaeNode>):Vector.<DaeAnimatedObject> {
// Словарь, в котором ключ-нода, значение-позиция в векторе nodes
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.<DaeAnimatedObject> = new Vector.<DaeAnimatedObject>(count);
for (i = 0; i < count; i++) {
var node:DaeNode = nodes[i];
if (isRootJointNode(node, nodesDictionary)) {
var animatedJoint:DaeAnimatedObject = addRootJointToSkin(skin, skinNode, node);
if (animatedJoint != null) {
animatedJoints[i] = animatedJoint;
addJointChildren(Joint(animatedJoint.object), animatedJoints, node, nodesDictionary);
}
}
}
return animatedJoints;
}
/**
* Возвращает <code>true</code> если у кости нет родительской кости
* @param node нода кости
* @param nodes словарь, в котором ключи это ноды всех костей
*/
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;
}
/**
* Добавляет рутовую кость к скину
*/
private function addRootJointToSkin(skin:Skin, skinNode:DaeNode, node:DaeNode):DaeAnimatedObject {
var joint:Joint;
if (skinNode == node) {
// Кость и является скином
joint = new Joint();
joint.name = node.name;
skin.addJoint(joint);
return new DaeAnimatedObject(joint);
} else {
if (node.scene == skinNode.scene) {
var parent:DaeNode = node.parent;
var toSceneMatrix:Matrix3D;
if (parent != null) {
// Считаем матрицу перевода кости в сцену
toSceneMatrix = parent.getMatrix();
while ((parent = parent.parent) != null) {
toSceneMatrix.append(parent.getMatrix());
}
}
// Считаем матрицу перевода локального пространства скина в сцену
var skinMatrix:Matrix3D = skinNode.getMatrix();
for (parent = skinNode.parent; parent != null; parent = parent.parent) {
skinMatrix.append(parent.getMatrix());
}
skinMatrix.invert();
// Считаем матрицу перевода в скин
var toSkinMatrix:Matrix3D;
if (toSceneMatrix != null) {
toSkinMatrix = toSceneMatrix;
toSkinMatrix.append(skinMatrix);
} else {
toSkinMatrix = skinMatrix;
}
var animation:ObjectAnimation = node.parseAnimation();
if (animation != null) {
// Если кость анимирована, создаем вспомогательную кость перевода в скин
var additionalJoint:Joint = new Joint();
additionalJoint.setMatrix(toSkinMatrix);
skin.addJoint(additionalJoint);
joint = new Joint();
joint.name = node.name;
additionalJoint.addJoint(joint);
return node.applyAnimation(node.applyTransformations(joint), animation);
} else {
joint = new Joint();
joint.name = node.name;
skin.addJoint(joint);
node.applyTransformations(joint, null, toSkinMatrix);
return new DaeAnimatedObject(joint);
}
} else {
// Не обрабатывается
document.logger.logJointInAnotherSceneError(node.data);
return null;
}
}
}
/**
* Создает иерархию дочерних костей и добавляет к родительской кости.
*
* @param parent родительская кость
* @param animatedJoints вектор костей в который положить созданные кости.
* В конец вектора будут добавлены вспомогательные кости, если понадобятся.
* @param parentNode нода родительской кости
* @param nodes словарь, в котором ключ это нода кости, а значение это индекс кости в векторе animatedJoints
*/
private function addJointChildren(parent:Joint, animatedJoints:Vector.<DaeAnimatedObject>, parentNode:DaeNode, nodes:Dictionary):void {
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 = child.name;
animatedJoints[nodes[child]] = child.applyAnimation(child.applyTransformations(joint));
parent.addJoint(joint);
addJointChildren(joint, animatedJoints, child, nodes);
} else {
// Нода не является костью
if (hasJointInDescendants(child, nodes)) {
// Если среди ее потомков есть кость, нужно создать вспомогательную кость вместо этой ноды.
joint = new Joint();
joint.name = child.name;
// Добавляем в конец новую кость
animatedJoints.push(child.applyAnimation(child.applyTransformations(joint)));
parent.addJoint(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;
}
/**
* Трансформирует все вершины объекта при помощи BindShapeMatrix из коллады
*/
private function applyBindShapeMatrix(skin:Skin):void {
var matrixXML:XML = data.skin.bind_shape_matrix[0];
if (matrixXML != null) {
var matrix:Array = parseNumbersArray(matrixXML);
if (matrix.length >= 16) {
var a:Number = matrix[0];
var b:Number = matrix[1];
var c:Number = matrix[2];
var d:Number = matrix[3];
var e:Number = matrix[4];
var f:Number = matrix[5];
var g:Number = matrix[6];
var h:Number = matrix[7];
var i:Number = matrix[8];
var j:Number = matrix[9];
var k:Number = matrix[10];
var l:Number = matrix[11];
for (var vertex:Vertex = skin.vertexList; vertex != null; vertex = vertex.next) {
var x:Number = vertex.x;
var y:Number = vertex.y;
var z:Number = vertex.z;
vertex.x = a*x + b*y + c*z + d;
vertex.y = e*x + f*y + g*z + h;
vertex.z = i*x + j*y + k*z + l;
}
}
}
}
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;
}
/**
* Находит ноду по ее сиду в векторе скелетов
*/
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;
}
/**
* Возвращает вектор нод костей.
*/
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) {
// Ошибка, нет ноды
}
nodes[i] = node;
}
return nodes;
}
} else {
document.logger.logNotFoundError(jointsXML.@source[0]);
}
}
return null;
}
}
}

View File

@@ -0,0 +1,295 @@
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.alternativa3d;
/**
* @private
*/
public class DaeDocument {
use namespace collada;
use namespace alternativa3d;
public var scene:DaeVisualScene;
/**
* Файл коллады
*/
private var data:XML;
// Словари для хранения соответствий id->DaeElement
internal var sources:Object;
internal var arrays:Object;
internal var vertices:Object;
internal var geometries:Object;
internal var nodes:Object;
internal var cameras:Object;
internal var images:Object;
internal var effects:Object;
internal var controllers:Object;
internal var samplers:Object;
internal var alternativa3DObjects:Object;
public var materials:Object;
internal var logger:DaeLogger;
public var versionMajor:uint;
public var versionMinor:uint;
public var alternativa3DExtensionVersionMajor:uint = 0;
public var alternativa3DExtensionVersionMinor:uint = 0;
public function DaeDocument(document:XML) {
this.data = document;
var versionComponents:Array = data.@version[0].toString().split(/[.,]/);
versionMajor = parseInt(versionComponents[1], 10);
versionMinor = parseInt(versionComponents[2], 10);
logger = new DaeLogger();
constructStructures();
constructScenes();
constructInstanceControllers();
constructAnimations();
constructAlternativa3DObjects();
}
private function isLocalURL(url:String):Boolean {
return url.charAt(0) == "#";
}
private function getLocalID(url:XML):String {
var path:String = url.toString();
if (isLocalURL(path)) {
var i:int = path.lastIndexOf("#");
return (i >= 0) ? path.substring(i + 1) : null;
} else {
logger.logExternalError(url);
return null;
}
}
// Ищем объявления всех элементов и заполняем словари
private function constructStructures():void {
var element:XML;
sources = new Object();
arrays = new Object();
for each (element in data..source) {
// Собираем все <source>. В конструкторах заполняется словарь arrays
var source:DaeSource = new DaeSource(element, this);
if (source.id != null) {
sources[source.id] = source;
}
}
cameras = new Object();
for each (element in data.library_cameras.camera) {
// Собираем все <camera>.
var camera:DaeCamera = new DaeCamera(element, this);
if (camera.id != null) {
cameras[camera.id] = camera;
}
}
images = new Object();
for each (element in data.library_images.image) {
// Собираем все <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) {
// Собираем все <effect>. В конструкторах заполняется словарь images
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) {
// Собираем все <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) {
// Собираем все <geometry>. В конструкторах заполняется словарь vertices
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) {
// Собираем все <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) {
// Создаем только корневые ноды, остальные создаются рекурсивно в конструкторах
var node:DaeNode = new DaeNode(element, this);
if (node.id != null) {
nodes[node.id] = node;
}
}
}
private function constructInstanceControllers():void {
for each (var node:DaeNode in nodes) {
var instanceControllerXML:XML = node.data.instance_controller[0];
if (instanceControllerXML != null) {
node.skinOrRootJoint = true;
var instanceController:DaeInstanceController = new DaeInstanceController(instanceControllerXML, this, node);
var jointNodes:Vector.<DaeNode> = instanceController.findRootJointNodes();
var i:int;
var count:int = jointNodes.length;
if (count > 0) {
var jointNode:DaeNode = jointNodes[0];
jointNode.addInstanceController(instanceController);
node.rootJoint = jointNode;
for (i = 0; i < count; i++) {
jointNodes[i].skinOrRootJoint = true;
}
}
}
}
}
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) {
// Создаем visual_scene, в конструкторах создаются node
var vscene:DaeVisualScene = new DaeVisualScene(element, this);
if (vscene.id == vsceneID) {
this.scene = vscene;
}
}
if (vsceneID != null && scene == null) {
logger.logNotFoundError(vsceneURL);
}
}
private function constructAnimations():void {
var element:XML;
samplers = new Object();
for each (element in data.library_animations..sampler) {
// Собираем все <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);
}
}
}
private function constructAlternativa3DObjects():void {
alternativa3DObjects = new Object();
var alternativa3dXML:XML = data.extra.technique.(@profile = "Alternativa3D")[0];
if (alternativa3dXML != null) {
var versionComponents:Array = alternativa3dXML.version[0].text().toString().split(/[.,]/);
alternativa3DExtensionVersionMajor = parseInt(versionComponents[0], 10);
alternativa3DExtensionVersionMinor = parseInt(versionComponents[1], 10);
var element:XML;
var object:DaeAlternativa3DObject;
for each (element in alternativa3dXML.library_containers.children()) {
// контейнеры
object = new DaeAlternativa3DObject(element, this);
if (object.id != null) {
alternativa3DObjects[object.id] = object;
}
}
for each (element in alternativa3dXML.library_sprites.sprite) {
// спрайты
object = new DaeAlternativa3DObject(element, this);
if (object.id != null) {
alternativa3DObjects[object.id] = object;
}
}
for each (element in alternativa3dXML.library_lods.lod) {
// лоды
object = new DaeAlternativa3DObject(element, this);
if (object.id != null) {
alternativa3DObjects[object.id] = object;
}
}
} else {
alternativa3DExtensionVersionMajor = alternativa3DExtensionVersionMinor = 0;
}
}
public function findArray(url:XML):DaeArray {
return arrays[getLocalID(url)];
}
public function findSource(url:XML):DaeSource {
return sources[getLocalID(url)];
}
public function findCamera(url:XML):DaeCamera {
return cameras[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)];
}
public function findAlternativa3DObject(url:XML):DaeAlternativa3DObject {
return alternativa3DObjects[getLocalID(url)];
}
}
}

View File

@@ -0,0 +1,157 @@
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.materials.FillMaterial;
import alternativa.engine3d.materials.Material;
import alternativa.engine3d.materials.TextureMaterial;
/**
* @private
*/
public class DaeEffect extends DaeElement {
use namespace collada;
private var effectParams:Object;
private var commonParams:Object;
private var techniqueParams:Object;
private var diffuse:DaeEffectParam;
private var emission:DaeEffectParam;
private var transparent:DaeEffectParam;
private var transparency:DaeEffectParam;
public function DaeEffect(data:XML, document:DaeDocument) {
super(data, document);
// Внтри <effect> объявляются image.
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) {
if (shader.localName() == "constant") {
var emissionXML:XML = shader.emission[0];
if (emissionXML != null) {
emission = new DaeEffectParam(emissionXML, this);
}
} else {
var diffuseXML:XML = shader.diffuse[0];
if (diffuseXML != null) {
diffuse = new DaeEffectParam(diffuseXML, 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);
}
}
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;
}
}
/**
* Возвращает материал движка с заданными параметрами.
* Перед использованием вызвать parse().
*/
public function getMaterial(setparams:Object):Material {
var diffuse:DaeEffectParam = (diffuse != null) ? diffuse : emission;
if (diffuse != null) {
var color:Array = diffuse.getColor(setparams);
if (color != null) {
var fillMaterial:FillMaterial = new FillMaterial(float4ToUint(color, false), color[3]);
if (transparency != null) {
var value:Number = transparency.getFloat(setparams);
if (!isNaN(value)) {
fillMaterial.alpha = value;
}
}
return fillMaterial;
} else {
var image:DaeImage = diffuse.getImage(setparams);
if (image != null) {
var sampler:DaeParam = diffuse.getSampler(setparams);
var textureMaterial:TextureMaterial = new TextureMaterial();
textureMaterial.repeat = (sampler == null) ? true : (sampler.wrap_s == null || sampler.wrap_s == "WRAP");
textureMaterial.diffuseMapURL = image.init_from;
var transparentImage:DaeImage = (transparent == null) ? null : transparent.getImage(setparams);
if (transparentImage != null) {
textureMaterial.opacityMapURL = transparentImage.init_from;
}
return textureMaterial;
}
}
}
return null;
}
/**
* Имя текстурного канала для карты цвета объекта
* Перед использованием вызвать parse().
*/
public function get diffuseTexCoords():String {
return (diffuse == null && emission == null) ? null : ((diffuse != null) ? diffuse.texCoord : emission.texCoord);
}
}
}

View File

@@ -0,0 +1,85 @@
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 {
// Возможно файл был экспортирован стандартным экспортом макса, который забивает на спецификацию и хранит ссылку прямо на 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,94 @@
package alternativa.engine3d.loaders.collada {
/**
* @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;
public function DaeElement(data:XML, document:DaeDocument) {
this.document = document;
this.data = data;
}
/**
* Выполняет предварительную настройку объекта.
*
* @return <code>false</code> в случае ошибки.
*/
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;
}
/**
* Переопределяемый метод parse()
*/
protected function parseImplementation():Boolean {
return true;
}
/**
* Возвращает массив значений типа String.
*/
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,125 @@
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Vertex;
import alternativa.engine3d.objects.Mesh;
/**
* @private
*/
public class DaeGeometry extends DaeElement {
use namespace collada;
use namespace alternativa3d;
private var primitives:Vector.<DaePrimitive>;
private var vertices:DaeVertices;
public function DaeGeometry(data:XML, document:DaeDocument) {
super(data, document);
// Внутри <geometry> объявляются элементы: sources, vertices.
// sources мы создаем внутри DaeDocument, здесь не нужно.
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) {
return parsePrimitives();
}
return false;
}
private function parsePrimitives():Boolean {
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":
primitives.push(new DaePrimitive(child, document));
break;
}
}
return true;
}
/**
* Создает геометрию и возвращает в виде меша.
* Перед использованием вызвать parse().
*
* @param materials словарь материалов.
*/
public function parseMesh(materials:Object):Mesh {
if (data.mesh.length() > 0) {
var mesh:Mesh = parseAlternativa3DObject(false);
if (mesh == null) {
mesh = new Mesh();
}
fillInMesh(mesh, materials);
cleanVertices(mesh);
mesh.calculateNormals(true);
mesh.calculateBounds();
return mesh;
}
return null;
}
/**
* Заполняет заданный объект геометрией и возвращает массив вершин с индексами.
* Перед использованием вызвать parse().
* Некоторые вершины в поле value содержат ссылку на дубликат вершины.
* После использования нужно вызвать cleanVertices для зачистки вершин от временных данных.
*
* @return массив вершин с индексами. У вершины в поле value задается дубликат вершины.
*/
public function fillInMesh(mesh:Mesh, materials:Object):Vector.<Vertex> {
vertices.parse();
var createdVertices:Vector.<Vertex> = vertices.fillInMesh(mesh);
for (var i:int = 0, count:int = primitives.length; i < count; i++) {
var primitive:DaePrimitive = primitives[i];
primitive.parse();
if (primitive.verticesEquals(vertices)) {
primitive.fillInMesh(mesh, createdVertices, materials[primitive.materialSymbol]);
} else {
// Ошибка, нельзя использовать вершины из другой геометрии
}
}
return createdVertices;
}
/**
* Зачищает вершины от временных данных
*/
public function cleanVertices(mesh:Mesh):void {
for (var vertex:Vertex = mesh.vertexList; vertex != null; vertex = vertex.next) {
vertex.index = 0;
vertex.value = null;
}
}
public function parseAlternativa3DObject(skin:Boolean = false):Mesh {
var profile:XML = data.mesh.extra.technique.(@profile == "Alternativa3D")[0];
if (profile != null) {
var meshXML:XML = profile.mesh[0];
if (meshXML != null) {
return (new DaeAlternativa3DObject(meshXML, document)).parseMesh(skin);
}
}
return null;
}
}
}

View File

@@ -0,0 +1,27 @@
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,53 @@
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);
}
/**
* Если DaeSource по ссылке source имеет тип значений Number и
* количество компонент не меньше заданного, то этот метод его вернет.
*/
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 {
// document.logger.logNotEnoughDataError();
}
} else {
document.logger.logNotFoundError(data.@source[0]);
}
return null;
}
}
}

View File

@@ -0,0 +1,55 @@
package alternativa.engine3d.loaders.collada {
/**
* @private
*/
public class DaeInstanceController extends DaeElement {
use namespace collada;
public var node:DaeNode;
public var skin:DaeAnimatedObject;
public function DaeInstanceController(data:XML, document:DaeDocument, node:DaeNode) {
super(data, document);
this.node = node;
}
public function get controller():DaeController {
var controller:DaeController = document.findController(data.@url[0]);
if (controller == null) {
document.logger.logNotFoundError(data.@url[0]);
}
return controller;
}
public 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 findRootJointNodes():Vector.<DaeNode> {
var controller:DaeController = this.controller;
if (controller != null) {
return controller.findRootJointNodes(this.skeletons);
}
return null;
}
}
}

View File

@@ -0,0 +1,39 @@
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,53 @@
package alternativa.engine3d.loaders.collada {
/**
* @private
*/
public class DaeLogger {
public function DaeLogger() {
}
private function logMessage(message:String, element:XML):void {
// var index:int = element.childIndex();
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,61 @@
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.materials.Material;
/**
* @private
*/
public class DaeMaterial extends DaeElement {
use namespace collada;
/**
* Материал движка.
* Перед использованием вызвать parse().
*/
public var material:Material;
/**
* Имя текстурного канала для карты цвета объекта
* Перед использованием вызвать parse().
*/
public var diffuseTexCoords:String;
/**
* Материал используется.
*/
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());
diffuseTexCoords = effect.diffuseTexCoords;
if (material != null) {
material.name = name;
}
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,458 @@
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.animation.ComplexAnimation;
import alternativa.engine3d.animation.MatrixAnimation;
import alternativa.engine3d.animation.ObjectAnimation;
import alternativa.engine3d.animation.TransformAnimation;
import alternativa.engine3d.core.Camera3D;
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;
use namespace alternativa3d;
public var scene:DaeVisualScene;
public var parent:DaeNode;
// Скин или рутовая кость
public var skinOrRootJoint:Boolean = false;
// Ссылка на рутовую кость скина, если есть
public var rootJoint:DaeNode;
/**
* Анимационные каналы этой ноды
*/
private var channels:Vector.<DaeChannel>;
/**
* Вектор контроллеров, которые ссылаются на эту ноду
*/
private var instanceControllers:Vector.<DaeInstanceController>;
/**
* Массив нод в этой ноде.
*/
public var nodes:Vector.<DaeNode>;
/**
* Массив объектов в этой ноде.
* Перед использованием вызвать parse().
*/
public var objects:Vector.<DaeAnimatedObject>;
/**
* Вектор скинов в этой ноде.
* Перед использованием вызвать parse().
*/
public var skins:Vector.<DaeAnimatedObject>;
/**
* Создание ноды из xml. Рекурсивно создаются дочерние ноды.
*/
public function DaeNode(data:XML, document:DaeDocument, scene:DaeVisualScene = null, parent:DaeNode = null) {
super(data, document);
this.scene = scene;
this.parent = parent;
// Внутри <node> объявляются другие 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;
}
}
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;
}
/**
* Возвращает ноду по сиду.
*/
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;
}
/**
* Парсит и возвращает массив скинов, связанных с этой нодой.
*/
public function parseSkins():Vector.<DaeAnimatedObject> {
if (instanceControllers == null) {
return null;
}
var skins:Vector.<DaeAnimatedObject> = new Vector.<DaeAnimatedObject>();
for (var i:int = 0, count:int = instanceControllers.length; i < count; i++) {
var instanceController:DaeInstanceController = instanceControllers[i];
var controller:DaeController = instanceController.controller;
if (controller != null) {
controller.parse();
var animatedSkinAndJoints:DaeAnimatedObject = controller.parseSkin(this, parseInstanceMaterials(instanceController.data), instanceController.skeletons);
if (animatedSkinAndJoints != null) {
var skin:Skin = Skin(animatedSkinAndJoints.object);
// Имя берем из ноды, содержащей instance_controller
skin.name = instanceController.node.name;
var animatedSkin:DaeAnimatedObject = applyAnimation(applyTransformations(skin));
if (animatedSkin.animation != null) {
if (animatedSkinAndJoints.animation != null) {
var complex:ComplexAnimation = new ComplexAnimation();
complex.addAnimation(animatedSkin.animation);
complex.addAnimation(animatedSkinAndJoints.animation);
animatedSkin.animation = complex;
}
} else {
animatedSkin.animation = animatedSkinAndJoints.animation;
}
skins.push(animatedSkin);
}
}
}
return (skins.length > 0) ? skins : null;
}
private function getNewName(index:int = 0):String {
var name:String = this.name;
if (name != null) {
if (index == 0) {
return name;
} else {
return name + "-" + index;
}
}
return null;
}
/**
* Парсит и возвращает массив объектов, связанных с этой нодой.
* Может быть Mesh или Object3D, если неизвестен тип объекта.
*/
public function parseObjects():Vector.<DaeAnimatedObject> {
var objects:Vector.<DaeAnimatedObject> = new Vector.<DaeAnimatedObject>();
if (isAlternativa3DObject()) {
var a3dObject:Object3D = parseAlternativa3DObject();
if (a3dObject != null) {
a3dObject.name = name;
objects.push(applyAnimation(applyTransformations(a3dObject)));
return objects;
}
} else {
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 "instance_camera":
var cam:DaeCamera = document.findCamera(child.@url[0]);
var camera:Camera3D = cam.parseCamera();
camera.name = getNewName(objects.length);
// Поворачиваем на 180 градусов по оси X, чтобы соответствовало движку.
var rotXMatrix:Matrix3D = new Matrix3D();
rotXMatrix.appendRotation(180, Vector3D.X_AXIS);
objects.push(applyAnimation(applyTransformations(camera, rotXMatrix)));
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 = getNewName(objects.length);
objects.push(applyAnimation(applyTransformations(mesh)));
}
} else {
document.logger.logNotFoundError(child.@url[0]);
}
break;
// case "instance_controller":
// Парсится в методе parseSkins();
// break;
case "instance_node":
document.logger.logInstanceNodeError(child);
// var instanceNode:DaeNode = document.findNode(child.@url[0]);
// instanceNode.parse();
// if (instanceNode != null) {
// var instances:Vector.<Object3D> = instanceNode.parseObjects();
// for (var j:int = 0, num:int = instances.length; j < num; j++) {
// var instance:Object3D = instances[j];
// objects.push(applyTransformationsAndAnimation(instance, instance.getMatrix()));
// }
// } else {
// document.logger.logNotFoundError(child.@url[0]);
// }
break;
}
}
}
// if (objects.length == 0) {
// // Не нашлось ни одного подходящего объекта, создаем Object3D.
// var object:Object3D = new Object3D();
// object.name = name;
// objects.push(applyAnimation(applyTransformations(object)));
// }
return (objects.length > 0) ? objects : null;
}
/**
* Возвращает трансформацию ноды в виде матрицы
*
* @param initialMatrix матрица, к которой будет добавлена трансформация ноды
*/
public 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--) {
// Трансформации накладываются с конца в начало
var child:XML = children[i];
var sid:XML = child.@sid[0];
if (sid != null && sid.toString() == "post-rotationY") {
// Стандартный экспорт макса записал какой-то хлам, игнорируем
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], components[1], components[2]);
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] ,components[7], components[11], components[15]])));
break;
}
case "lookat" : {
// components = parseNumbersArray(child);
break;
}
case "skew" : {
document.logger.logSkewError(child);
break;
}
}
}
return matrix;
}
/**
* Назначает контроллер анимации к объекту.
*
* @param animation анимация которую следует применить к объекту,
* если <code>null</code>, будет создана новая анимация из ноды.
*/
public function applyAnimation(object:Object3D, animation:ObjectAnimation = null):DaeAnimatedObject {
animation = (animation == null) ? parseAnimation() : animation;
if (animation != null) {
animation.object = object;
}
return new DaeAnimatedObject(object, animation);
}
/**
* Применяет трансформацию к объекту.
*
* @param prepend если не равен <code>null</code>, трансформация добавляется к этой матрице.
*/
public function applyTransformations(object:Object3D, prepend:Matrix3D = null, append:Matrix3D = null):Object3D {
if (append != null) {
var matrix:Matrix3D = getMatrix(prepend);
matrix.append(append);
object.setMatrix(matrix);
} else {
object.setMatrix(getMatrix(prepend));
}
return object;
}
private function isAlternativa3DObject():Boolean {
return data.extra.technique.(@profile == "Alternativa3D")[0] != null;
}
private function parseAlternativa3DObject():Object3D {
var profile:XML = data.extra.technique.(@profile == "Alternativa3D")[0];
if (profile != null) {
var containerXML:XML = profile.instance_container[0];
if (containerXML != null) {
var container:DaeAlternativa3DObject = document.findAlternativa3DObject(containerXML.@url[0]);
if (container != null) {
return container.parseContainer();
} else {
document.logger.logNotFoundError(containerXML.@url[0])
}
}
var spriteXML:XML = profile.instance_sprite[0];
if (spriteXML != null) {
var sprite:DaeAlternativa3DObject = document.findAlternativa3DObject(spriteXML.@url[0]);
if (sprite != null) {
var material:DaeMaterial = document.findMaterial(spriteXML.instance_material.@target[0]);
if (material != null) {
material.parse();
material.used = true;
return sprite.parseSprite3D(material.material);
} else {
return sprite.parseSprite3D();
}
} else {
document.logger.logNotFoundError(spriteXML.@url[0])
}
}
var lodXML:XML = profile.instance_lod[0];
if (lodXML != null) {
var lod:DaeAlternativa3DObject = document.findAlternativa3DObject(lodXML.@url[0]);
if (lod != null) {
return lod.parseLOD();
} else {
document.logger.logNotFoundError(lodXML.@url[0]);
}
}
}
return null;
}
/**
* Возвращает анимацию ноды.
*/
public function parseAnimation():ObjectAnimation {
if (channels == null) {
return null;
}
var channel:DaeChannel = channels[0];
channel.parse();
if (channel.animatedParam == DaeChannel.PARAM_MATRIX) {
// Анимация матрицы
var matrixAnimation:MatrixAnimation = new MatrixAnimation();
matrixAnimation.matrix = channel.track;
return matrixAnimation;
}
// Это не анимация матрицы, значит покомпонентная анимация
var animation:TransformAnimation = new TransformAnimation();
var count:int = channels.length;
for (var i:int = 0; i < count; i++) {
channel = channels[i];
channel.parse();
switch (channel.animatedParam) {
case DaeChannel.PARAM_TRANSLATE:
animation.translation = channel.track;
break;
case DaeChannel.PARAM_TRANSLATE_X:
animation.x = channel.track;
break;
case DaeChannel.PARAM_TRANSLATE_Y:
animation.y = channel.track;
break;
case DaeChannel.PARAM_TRANSLATE_Z:
animation.z = channel.track;
break;
case DaeChannel.PARAM_ROTATION_X:
animation.rotationX = channel.track;
break;
case DaeChannel.PARAM_ROTATION_Y:
animation.rotationY = channel.track;
break;
case DaeChannel.PARAM_ROTATION_Z:
animation.rotationZ = channel.track;
break;
case DaeChannel.PARAM_SCALE:
animation.scale = channel.track;
break;
case DaeChannel.PARAM_SCALE_X:
animation.scaleX = channel.track;
break;
case DaeChannel.PARAM_SCALE_Y:
animation.scaleY = channel.track;
break;
case DaeChannel.PARAM_SCALE_Z:
animation.scaleZ = channel.track;
break;
}
}
return animation;
}
}
}

View File

@@ -0,0 +1,79 @@
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;
}
/**
* Возвращает sid параметра с типом surface. Только если тип этого элемента sampler2D и версия коллады 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,266 @@
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.core.Vertex;
import alternativa.engine3d.materials.Material;
import alternativa.engine3d.objects.Mesh;
/**
* @private
*/
public class DaePrimitive extends DaeElement {
use namespace collada;
private var verticesInput:DaeInput;
private var texCoordsInputs:Vector.<DaeInput>;
private var inputsStride:int;
public function DaePrimitive(data:XML, document:DaeDocument) {
super(data, document);
}
override protected function parseImplementation():Boolean {
parseInputs();
return true;
}
private function parseInputs():void {
texCoordsInputs = 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;
}
}
var offset:int = input.offset;
maxInputOffset = (offset > maxInputOffset) ? offset : maxInputOffset;
}
inputsStride = maxInputOffset + 1;
}
private function findTexCoordsInput(setNum:int):DaeInput {
for (var i:int = 0, count:int = texCoordsInputs.length; i < count; i++) {
var texCoordsInput:DaeInput = texCoordsInputs[i];
if (texCoordsInput.setNum == setNum) {
return texCoordsInput;
}
}
return (texCoordsInputs.length > 0) ? texCoordsInputs[0] : null;
}
private function get type():String {
return data.localName() as String;
}
/**
* Заполняет заданный меш геометрией этого примитива, используя заданные вершины.
* На вершины накладывается uv маппинг.
* Перед использованием вызвать parse().
*/
public function fillInMesh(mesh:Mesh, vertices:Vector.<Vertex>, instanceMaterial:DaeInstanceMaterial = null):void {
var countXML:XML = data.@count[0];
if (countXML == null) {
document.logger.logNotEnoughDataError(data);
return;
}
var numPrimitives:int = parseInt(countXML.toString(), 10);
var texCoordsInput:DaeInput;
var material:Material;
if (instanceMaterial != null) {
var dmat:DaeMaterial = instanceMaterial.material;
dmat.parse();
if (dmat.diffuseTexCoords != null) {
texCoordsInput = findTexCoordsInput(instanceMaterial.getBindVertexInputSetNum(dmat.diffuseTexCoords));
} else {
texCoordsInput = findTexCoordsInput(-1);
}
dmat.used = true;
material = dmat.material;
} else {
texCoordsInput = findTexCoordsInput(-1);
}
if (texCoordsInput != null) {
// Если у вершины index != -1, значит вершина где-то используется и ее нужно сдублировать
// Устанавливаем для такой вершины index в -2
for each (var vertex:Vertex in vertices) {
while (vertex != null && vertex.index != -1) {
vertex.index = -2;
// Переходим к следующему дубликату
vertex = vertex.value;
}
}
}
var texCoords:Vector.<Number>;
var texCoordsStride:int = 1;
var texCoordsOffset:int = 0;
if (texCoordsInput != null) {
var texCoordsSource:DaeSource = texCoordsInput.prepareSource(2);
if (texCoordsSource != null) {
texCoords = texCoordsSource.numbers;
texCoordsStride = texCoordsSource.stride;
texCoordsOffset = texCoordsInput.offset;
}
}
var indicesXML:XML;
var indices:Array;
switch (this.type) {
case "polygons" : {
if (data.ph.length() > 0) {
// Полигоны с дырками не поддерживаются
// document.logger.lo
}
var indicesList:XMLList = data.p;
for (var i:int = 0, count:int = indicesList.length(); i < count; i++) {
indices = parseIntsArray(indicesList[i]);
fillInPolygon(mesh, material, vertices, verticesInput.offset, indices.length/inputsStride, indices, texCoords, texCoordsStride, texCoordsOffset);
}
break;
}
case "polylist" : {
indicesXML = data.p[0];
if (indicesXML == null) {
document.logger.logNotEnoughDataError(data);
return;
}
indices = parseIntsArray(indicesXML);
var vcountsXML:XML = data.vcount[0];
var vcounts:Array;
if (vcountsXML != null) {
vcounts = parseIntsArray(vcountsXML);
if (vcounts.length < numPrimitives) {
return;
}
fillInPolylist(mesh, material, vertices, verticesInput.offset, numPrimitives, indices, vcounts, texCoords, texCoordsStride, texCoordsOffset);
} else {
fillInPolygon(mesh, material, vertices, verticesInput.offset, numPrimitives, indices, texCoords, texCoordsStride, texCoordsOffset);
}
break;
}
case "triangles" : {
indicesXML = data.p[0];
if (indicesXML == null) {
document.logger.logNotEnoughDataError(data);
return;
}
indices = parseIntsArray(indicesXML);
fillInTriangles(mesh, material, vertices, verticesInput.offset, numPrimitives, indices, texCoords, texCoordsStride, texCoordsOffset);
break;
}
}
}
/**
* Добавляет uv координаты вершине или создает новую вершину, если нельзя добавить в эту.
* Новая вершина добавляется в список value этой вершины.
* @return вершина с заданными uv координатами
*/
private function applyUV(mesh:Mesh, vertex:Vertex, texCoords:Vector.<Number>, index:int):Vertex {
var u:Number = texCoords[index];
var v:Number = 1 - texCoords[int(index + 1)];
if (vertex.index == -1) {
// Была без uv координат
vertex.u = u;
vertex.v = v;
vertex.index = index;
return vertex;
}
if (vertex.index == index) {
return vertex;
} else {
// Дублируем вершину, если её index отличается от индекса заданной uv координаты
while (vertex.value != null) {
vertex = vertex.value;
if (vertex.index == index) {
return vertex;
}
}
// Последний элемент, создаем дубликат и возвращаем
vertex.value = mesh.addVertex(vertex.x, vertex.y, vertex.z, u, v);
vertex = vertex.value;
vertex.index = index;
return vertex;
}
}
/**
* Создает один полигон.
*/
private function fillInPolygon(mesh:Mesh, material:Material, vertices:Vector.<Vertex>, verticesOffset:int, numIndices:int, indices:Array, texCoords:Vector.<Number>, texCoordsStride:int = 1, texCoordsOffset:int = 0):void {
var polygon:Vector.<Vertex> = new Vector.<Vertex>(numIndices);
for (var i:int = 0; i < numIndices; i++) {
var vertex:Vertex = vertices[indices[int(inputsStride*i + verticesOffset)]];
if (texCoords != null) {
vertex = applyUV(mesh, vertex, texCoords, texCoordsStride*indices[int(inputsStride*i + texCoordsOffset)]);
}
polygon[i] = vertex;
}
mesh.addFace(polygon, material);
}
private function fillInPolylist(mesh:Mesh, material:Material, vertices:Vector.<Vertex>, verticesOffset:int, numFaces:int, indices:Array, vcounts:Array, texCoords:Vector.<Number> = null, texCoordsStride:int = 1, texCoordsOffset:int = 0):void {
var polygon:Vector.<Vertex> = new Vector.<Vertex>();
var polyIndex:int = 0;
for (var i:int = 0; i < numFaces; i++) {
var count:int = vcounts[i];
if (count >= 3) {
polygon.length = count;
for (var j:int = 0; j < count; j++) {
var vertexIndex:int = inputsStride*(polyIndex + j);
var vertex:Vertex = vertices[indices[int(vertexIndex + verticesOffset)]];
if (texCoords != null) {
vertex = applyUV(mesh, vertex, texCoords, texCoordsStride*indices[int(vertexIndex + texCoordsOffset)]);
}
polygon[j] = vertex;
}
polyIndex += count;
mesh.addFace(polygon, material);
}
}
}
private function fillInTriangles(mesh:Mesh, material:Material, vertices:Vector.<Vertex>, verticesOffset:int, numFaces:int, indices:Array, texCoords:Vector.<Number> = null, texCoordsStride:int = 1, texCoordsOffset:int = 0):void {
for (var i:int = 0; i < numFaces; i++) {
var index:int = 3*inputsStride*i;
var vertexIndex:int = index + verticesOffset;
var a:Vertex = vertices[indices[int(vertexIndex)]];
var b:Vertex = vertices[indices[int(vertexIndex + inputsStride)]];
var c:Vertex = vertices[indices[int(vertexIndex + 2*inputsStride)]];
if (texCoords != null) {
var texIndex:int = index + texCoordsOffset;
a = applyUV(mesh, a, texCoords, texCoordsStride*indices[int(texIndex)]);
b = applyUV(mesh, b, texCoords, texCoordsStride*indices[int(texIndex + inputsStride)]);
c = applyUV(mesh, c, texCoords, texCoordsStride*indices[int(texIndex + 2*inputsStride)]);
}
mesh.addTriFace(a, b, c, material);
}
}
/**
* Сравнивает вершины, используемые в примитиве с указанными
* Перед использованием вызвать parse().
*/
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();
}
}
}

View File

@@ -0,0 +1,104 @@
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.animation.MatrixKey;
import alternativa.engine3d.animation.PointKey;
import alternativa.engine3d.animation.Track;
import alternativa.engine3d.animation.ValueKey;
import flash.geom.Matrix3D;
/**
* @private
*/
public class DaeSampler extends DaeElement {
use namespace collada;
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 parseValuesTrack():Track {
if (times != null && values != null && timesStride > 0) {
var track:Track = new Track();
var count:int = times.length/timesStride;
for (var i:int = 0; i < count; i++) {
track.addKey(new ValueKey(times[int(timesStride*i)], values[int(valuesStride*i)]));
}
track.sortKeys();
// TODO:: Всякие исключительные ситуации с индексами
return track;
}
return null;
}
public function parseMatrixTrack():Track {
if (times != null && values != null && timesStride != 0) {
var track:Track = new Track();
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(new MatrixKey(times[i*timesStride], matrix));
}
track.sortKeys();
return track;
}
return null;
}
public function parsePointsTrack():Track {
if (times != null && values != null && timesStride != 0) {
var track:Track = new Track();
var count:int = times.length/timesStride;
for (var i:int = 0; i < count; i++) {
var index:int = i*valuesStride;
track.addKey(new PointKey(times[i*timesStride], values[index], values[index + 1], values[index + 2]));
}
track.sortKeys();
return track;
// TODO:: Всякие исключительные ситуации с индексами
}
return null;
}
}
}

View File

@@ -0,0 +1,153 @@
package alternativa.engine3d.loaders.collada {
/**
* @private
*/
public class DaeSource extends DaeElement {
use namespace collada;
/**
* Типы массивов
*/
private const FLOAT_ARRAY:String = "float_array";
private const INT_ARRAY:String = "int_array";
private const NAME_ARRAY:String = "Name_array";
/**
* Массив элементов типа Number.
* Перед использованием вызвать parse().
*/
public var numbers:Vector.<Number>;
/**
* Массив элементов типа int.
* Перед использованием вызвать parse().
*/
public var ints:Vector.<int>;
/**
* Массив элементов типа string.
* Перед использованием вызвать parse().
*/
public var names:Vector.<String>;
/**
* Размерность типов в массиве numbers или ints.
* Перед использованием вызвать parse().
*/
public var stride:int;
public function DaeSource(data:XML, document:DaeDocument) {
super(data, document);
// Внутри <source> объявляются arrays.
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++) {
// Только param, у которого установлен name, должен быть считан
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,58 @@
package alternativa.engine3d.loaders.collada {
import alternativa.engine3d.core.Vertex;
import alternativa.engine3d.objects.Mesh;
/**
* @private
*/
public class DaeVertices extends DaeElement {
use namespace collada;
/**
* Источник данных координат вершин. Содержит координаты в массиве numbers.
* Свойство stride источника не меньше трех.
* Перед использованием вызвать parse().
*/
private var positions:DaeSource;
//private var texCoords:Vector.<DaeSource>;
public function DaeVertices(data:XML, document:DaeDocument) {
super(data, document);
}
override protected function parseImplementation():Boolean {
// Получаем массив координат вершин
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;
}
/**
* Создает вершины в меше. У каждой вершины index устанавливается в -1.
* Перед использованием вызвать parse().
*
* @return вектор вершин и их индексов
*/
public function fillInMesh(mesh:Mesh):Vector.<Vertex> {
var stride:int = positions.stride;
var coords:Vector.<Number> = positions.numbers;
var numVerts:int = positions.numbers.length/stride;
var createdVertices:Vector.<Vertex> = new Vector.<Vertex>(numVerts);
var i:int;
for (i = 0; i < numVerts; i++) {
var offset:int = stride*i;
var vertex:Vertex = mesh.addVertex(coords[offset], coords[int(offset + 1)], coords[int(offset + 2)], 0, 0);
vertex.index = -1;
createdVertices[i] = vertex;
}
return createdVertices;
}
}
}

View File

@@ -0,0 +1,33 @@
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);
// Внутри <visual_scene> объявляются node.
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,7 @@
package alternativa.engine3d.loaders.collada {
/**
* @private
*/
public namespace collada = "http://www.collada.org/2005/11/COLLADASchema";
}

View File

@@ -0,0 +1,48 @@
package alternativa.engine3d.loaders.events {
import flash.events.ErrorEvent;
import flash.events.Event;
public class LoaderErrorEvent extends ErrorEvent {
public static const LOADER_ERROR:String = "loaderError";
private var _url:String;
/**
* Создаёт новый экземпляр.
* @param type тип события
* @param url адрес файла, при загрузке которого произошла проблема
* @param text описание ошибки
*/
public function LoaderErrorEvent(type:String, url:String, text:String) {
super(type);
this.text = text;
_url = url;
}
/**
* Адрес файла, при загрузке которого произошла проблема
*/
public function get url():String {
return _url;
}
/**
* Клонирует объект.
* @return клон объекта
*/
override public function clone():Event {
return new LoaderErrorEvent(type, _url, text);
}
/**
* Создаёт строковое представление объекта.
* @return строковое представление объекта
*/
override public function toString():String {
return "[LoaderErrorEvent url=" + _url + ", text=" + text + "]";
}
}
}

View File

@@ -0,0 +1,73 @@
package alternativa.engine3d.loaders.events {
import flash.events.Event;
/**
* Событие загрузчиков ресурсов, состоящих из нескольких частей.
*/
public class LoaderEvent extends Event {
/**
* Событие начала загрузки очередной части ресурса.
*/
public static const PART_OPEN:String = "partOpen";
/**
* Событие окончания загрузки очередной части ресурса.
*/
public static const PART_COMPLETE:String = "partComplete";
private var _partsTotal:int;
private var _currentPart:int;
private var _target:Object;
/**
* Создаёт новый экземпляр.
* @param type тип события
* @param partsTotal общее количество загружаемых частей
* @param currentPart номер части, к которому относится событие. Нумерация начинается с нуля
* @param target объект, к которому относится событие
*/
public function LoaderEvent(type:String, partsTotal:int, currentPart:int, target:Object = null) {
super(type);
_partsTotal = partsTotal;
_currentPart = currentPart;
_target = target;
}
/**
* Общее количество загружаемых частей.
*/
public function get partsTotal():int {
return _partsTotal;
}
/**
* Номер части, к которому относится событие. Нумерация начинается с нуля
*/
public function get currentPart():int {
return _currentPart;
}
/**
* Объект, содержащийся в событии
*/
override public function get target():Object {
return _target;
}
/**
* Клонирует объект.
* @return клон объекта
*/
override public function clone():Event {
return new LoaderEvent(type, _partsTotal, _currentPart, _target);
}
/**
* Создаёт строкове представление объекта.
* @return строкове представление объекта
*/
override public function toString():String {
return "[LoaderEvent type=" + type + ", partsTotal=" + _partsTotal + ", currentPart=" + _currentPart + ", target=" + _target + "]";
}
}
}

View File

@@ -0,0 +1,73 @@
package alternativa.engine3d.loaders.events {
import flash.events.Event;
import flash.events.ProgressEvent;
/**
* Событие прогресса загрузки ресурсов, состоящих из нескольких частей.
*/
public class LoaderProgressEvent extends ProgressEvent {
/**
* Событие прогресса загрузки очередной части ресурса.
*/
public static const LOADER_PROGRESS:String = "loaderProgress";
private var _filesTotal:int;
private var _filesLoaded:int;
private var _totalProgress:Number = 0;
/**
* Создаёт новый экземпляр.
* @param type тип события
* @param filesTotal общее количество загружаемых файлов
* @param filesLoaded количество полностью загруженных файлов
* @param totalProgress общий прогресс загрузки, выраженный числом в интервале [0, 1]
* @param bytesLoaded количество загруженных байт загружаемого в данный момент файла
* @param bytesTotal объём загружаемого в данный момент файла
*/
public function LoaderProgressEvent(type:String, filesTotal:int, filesLoaded:int, totalProgress:Number = 0, bytesLoaded:uint = 0, bytesTotal:uint = 0) {
super(type, false, false, bytesLoaded, bytesTotal);
_filesTotal = filesTotal;
_filesLoaded = filesLoaded;
_totalProgress = totalProgress;
}
/**
* Общее количество загружаемых файлов.
*/
public function get filesTotal():int {
return _filesTotal;
}
/**
* Количество полностью загруженных файлов.
*/
public function get filesLoaded():int {
return _filesLoaded;
}
/**
* Общий прогресс загрузки, выраженный числом в интервале [0, 1].
*/
public function get totalProgress():Number {
return _totalProgress;
}
/**
* Клонирует объект.
* @return клон объекта
*/
override public function clone():Event {
return new LoaderProgressEvent(type, _filesTotal, _filesLoaded, _totalProgress, bytesLoaded, bytesTotal);
}
/**
* Создаёт строкове представление объекта.
* @return строкове представление объекта
*/
override public function toString():String {
return "[LoaderProgressEvent filesTotal=" + _filesTotal + ", filesLoaded=" + _filesLoaded + ", totalProgress=" + _totalProgress.toFixed(2) + "]";
}
}
}

View File

@@ -0,0 +1,80 @@
package alternativa.engine3d.materials {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Canvas;
import alternativa.engine3d.core.Face;
import alternativa.engine3d.core.Vertex;
import alternativa.engine3d.core.Wrapper;
use namespace alternativa3d;
public class FillMaterial extends Material {
public var color:int;
public var alpha:Number;
public var lineThickness:Number;
public var lineColor:int;
public function FillMaterial(color:int = 0x7F7F7F, alpha:Number = 1, lineThickness:Number = -1, lineColor:int = 0xFFFFFF) {
this.color = color;
this.alpha = alpha;
this.lineThickness = lineThickness;
this.lineColor = lineColor;
}
override alternativa3d function draw(camera:Camera3D, canvas:Canvas, list:Face, distance:Number):void {
var viewSizeX:Number = camera.viewSizeX;
var viewSizeY:Number = camera.viewSizeY;
var next:Face;
// Отрисовка
if (lineThickness >= 0) canvas.gfx.lineStyle(lineThickness, lineColor);
for (var face:Face = list; face != null; face = next) {
next = face.processNext;
face.processNext = null;
var wrapper:Wrapper = face.wrapper;
var vertex:Vertex = wrapper.vertex;
if (alpha > 0) canvas.gfx.beginFill(color, alpha);
canvas.gfx.moveTo(vertex.cameraX*viewSizeX/vertex.cameraZ, vertex.cameraY*viewSizeY/vertex.cameraZ);
var numVertices:int = -1;
for (wrapper = wrapper.next; wrapper != null; wrapper = wrapper.next) {
vertex = wrapper.vertex;
canvas.gfx.lineTo(vertex.cameraX*viewSizeX/vertex.cameraZ, vertex.cameraY*viewSizeY/vertex.cameraZ);
numVertices++;
}
if (alpha <= 0) {
vertex = face.wrapper.vertex;
canvas.gfx.lineTo(vertex.cameraX*viewSizeX/vertex.cameraZ, vertex.cameraY*viewSizeY/vertex.cameraZ);
}
camera.numTriangles += numVertices;
camera.numPolygons++;
}
camera.numDraws++;
}
override alternativa3d function drawViewAligned(camera:Camera3D, canvas:Canvas, list:Face, distance:Number, a:Number, b:Number, c:Number, d:Number, tx:Number, ty:Number):void {
var viewSizeX:Number = camera.viewSizeX;
var viewSizeY:Number = camera.viewSizeY;
var next:Face;
// Отрисовка
if (lineThickness >= 0) canvas.gfx.lineStyle(lineThickness, lineColor);
for (var face:Face = list; face != null; face = next) {
next = face.processNext;
face.processNext = null;
var wrapper:Wrapper = face.wrapper;
var vertex:Vertex = wrapper.vertex;
if (alpha > 0) canvas.gfx.beginFill(color, alpha);
canvas.gfx.moveTo(vertex.cameraX*viewSizeX/distance, vertex.cameraY*viewSizeY/distance);
var numVertices:int = -1;
for (wrapper = wrapper.next; wrapper != null; wrapper = wrapper.next) {
vertex = wrapper.vertex;
canvas.gfx.lineTo(vertex.cameraX*viewSizeX/distance, vertex.cameraY*viewSizeY/distance);
numVertices++;
}
camera.numTriangles += numVertices;
camera.numPolygons++;
}
camera.numDraws++;
}
}
}

View File

@@ -0,0 +1,30 @@
package alternativa.engine3d.materials {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Canvas;
import alternativa.engine3d.core.Face;
use namespace alternativa3d;
public class Material {
public var name:String;
alternativa3d function draw(camera:Camera3D, canvas:Canvas, list:Face, distance:Number):void {
clearLinks(list);
}
alternativa3d function drawViewAligned(camera:Camera3D, canvas:Canvas, list:Face, distance:Number, a:Number, b:Number, c:Number, d:Number, tx:Number, ty:Number):void {
clearLinks(list);
}
alternativa3d function clearLinks(list:Face):void {
while (list != null) {
var next:Face = list.processNext;
list.processNext = null;
list = next;
}
}
}
}

View File

@@ -0,0 +1,565 @@
package alternativa.engine3d.materials {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Canvas;
import alternativa.engine3d.core.Face;
import alternativa.engine3d.core.Vertex;
import alternativa.engine3d.core.Wrapper;
import flash.display.BitmapData;
import flash.filters.ConvolutionFilter;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
use namespace alternativa3d;
public class TextureMaterial extends Material {
static private const filter:ConvolutionFilter = new ConvolutionFilter(2, 2, [1, 1, 1, 1], 4, 0, false, true);
static private const matrix:Matrix = new Matrix();
static private const rect:Rectangle = new Rectangle();
static private const point:Point = new Point();
static protected var drawVertices:Vector.<Number> = new Vector.<Number>();
static protected var drawUVTs:Vector.<Number> = new Vector.<Number>();
static protected var drawIndices:Vector.<int> = new Vector.<int>();
public var diffuseMapURL:String;
public var opacityMapURL:String;
private var _texture:BitmapData;
private var _mipMapping:int = 0;
private var mipMap:Vector.<BitmapData>;
private var numMaps:int = 0;
public var repeat:Boolean = false;
public var smooth:Boolean = true;
public var resolution:Number = 1;
public var threshold:Number = 0.01;
public var correctUV:Boolean = false;
public function TextureMaterial(texture:BitmapData = null, repeat:Boolean = false, smooth:Boolean = true, mipMapping:int = 0, resolution:Number = 1) {
_texture = texture;
this.repeat = repeat;
this.smooth = smooth;
_mipMapping = mipMapping;
if (_texture != null && _mipMapping > 0) {
calculateMipMaps();
}
this.resolution = resolution;
}
public function get texture():BitmapData {
return _texture;
}
public function set texture(value:BitmapData):void {
if (value != _texture) {
_texture = value;
disposeMipMaps();
if (value != null && _mipMapping > 0) {
calculateMipMaps();
}
}
}
public function get mipMapping():int {
return _mipMapping;
}
public function set mipMapping(value:int):void {
if (value < 0) value = 0;
if (value != _mipMapping) {
if (_texture != null) {
if (_mipMapping == 0 && value > 0) {
calculateMipMaps();
} else if (_mipMapping > 0 && value == 0) {
disposeMipMaps();
}
}
_mipMapping = value;
}
}
private function disposeMipMaps():void {
for (var i:int = 1; i < numMaps; i++) {
(mipMap[i] as BitmapData).dispose();
}
mipMap = null;
numMaps = 0;
}
private function calculateMipMaps(maxLevel:int = 12):void {
mipMap = new Vector.<BitmapData>();
numMaps = 0;
matrix.identity();
mipMap[numMaps] = _texture;
numMaps++;
filter.preserveAlpha = !_texture.transparent;
var bmp:BitmapData = (_texture.width*_texture.height > 16777215) ? _texture.clone() : new BitmapData(_texture.width, _texture.height, _texture.transparent);
var current:BitmapData = _texture;
var w:Number = rect.width = _texture.width;
var h:Number = rect.height = _texture.height;
while (numMaps <= maxLevel && w > 1 && h > 1 && rect.width > 1 && rect.height > 1) {
bmp.applyFilter(current, rect, point, filter);
rect.width = w >> 1;
rect.height = h >> 1;
matrix.a = rect.width/w;
matrix.d = rect.height/h;
w *= 0.5;
h *= 0.5;
current = new BitmapData(rect.width, rect.height, _texture.transparent, 0);
current.draw(bmp, matrix, null, null, null, false);
mipMap[numMaps] = current;
numMaps++;
}
bmp.dispose();
}
override alternativa3d function draw(camera:Camera3D, canvas:Canvas, list:Face, distance:Number):void {
var face:Face;
var next:Face;
var last:Face;
var wrapper:Wrapper;
var vertex:Vertex;
var a:int;
var b:int;
var c:int;
var t:Number;
var f:Number;
var j:int;
var mu:Number;
var mv:Number;
var du:Number;
var dv:Number;
var drawTexture:BitmapData;
var viewSizeX:Number = camera.viewSizeX;
var viewSizeY:Number = camera.viewSizeY;
var vertices:Vector.<Number> = drawVertices;
var uvts:Vector.<Number> = drawUVTs;
var indices:Vector.<int> = drawIndices;
var numVertices:int;
var verticesLength:int;
var uvtsLength:int;
var indicesLength:int;
var numDraws:int = camera.numDraws;
var numPolygons:int = camera.numPolygons;
var numTriangles:int = camera.numTriangles;
// Если нет текстуры, нужно просто расцепить список
if (_texture == null) {
clearLinks(list);
return;
}
// Мипмаппинг
if (_mipMapping < 2) {
numDraws++;
numVertices = 0;
verticesLength = 0;
uvtsLength = 0;
indicesLength = 0;
for (face = list; face != null; face = next) {
next = face.processNext;
face.processNext = null;
wrapper = face.wrapper;
vertex = wrapper.vertex;
if (vertex.drawID != numDraws) {
t = 1/vertex.cameraZ;
vertices[verticesLength] = vertex.cameraX*viewSizeX*t;
verticesLength++;
vertices[verticesLength] = vertex.cameraY*viewSizeY*t;
verticesLength++;
uvts[uvtsLength] = vertex.u;
uvtsLength++;
uvts[uvtsLength] = vertex.v;
uvtsLength++;
uvts[uvtsLength] = t;
uvtsLength++;
a = numVertices;
vertex.index = numVertices++;
vertex.drawID = numDraws;
} else {
a = vertex.index;
}
wrapper = wrapper.next;
vertex = wrapper.vertex;
if (vertex.drawID != numDraws) {
t = 1/vertex.cameraZ;
vertices[verticesLength] = vertex.cameraX*viewSizeX*t;
verticesLength++;
vertices[verticesLength] = vertex.cameraY*viewSizeY*t;
verticesLength++;
uvts[uvtsLength] = vertex.u;
uvtsLength++;
uvts[uvtsLength] = vertex.v;
uvtsLength++;
uvts[uvtsLength] = t;
uvtsLength++;
b = numVertices;
vertex.index = numVertices++;
vertex.drawID = numDraws;
} else {
b = vertex.index;
}
for (wrapper = wrapper.next; wrapper != null; wrapper = wrapper.next) {
vertex = wrapper.vertex;
if (vertex.drawID != numDraws) {
t = 1/vertex.cameraZ;
vertices[verticesLength] = vertex.cameraX*viewSizeX*t;
verticesLength++;
vertices[verticesLength] = vertex.cameraY*viewSizeY*t;
verticesLength++;
uvts[uvtsLength] = vertex.u;
uvtsLength++;
uvts[uvtsLength] = vertex.v;
uvtsLength++;
uvts[uvtsLength] = t;
uvtsLength++;
c = numVertices;
vertex.index = numVertices++;
vertex.drawID = numDraws;
} else {
c = vertex.index;
}
drawIndices[indicesLength] = a;
indicesLength++;
drawIndices[indicesLength] = b;
indicesLength++;
drawIndices[indicesLength] = c;
indicesLength++;
b = c;
numTriangles++;
}
numPolygons++;
}
// Подрезка
vertices.length = verticesLength;
uvts.length = uvtsLength;
indices.length = indicesLength;
// Отрисовка
if (_mipMapping == 0) {
// Без мипмаппинга
drawTexture = _texture;
} else {
// Мипмаппинг по удалённости объекта от камеры
f = camera.focalLength*resolution;
var level:int = (distance >= f) ? (1 + Math.log(distance/f)*1.442695040888963387) : 0;
if (level >= numMaps) level = numMaps - 1;
drawTexture = mipMap[level];
}
if (correctUV) {
du = -0.5/(drawTexture.width - 1);
dv = -0.5/(drawTexture.height - 1);
mu = 1 - du - du;
mv = 1 - dv - dv;
for (j = 0; j < uvtsLength; j++) {
uvts[j] = uvts[j]*mu + du; j++;
uvts[j] = uvts[j]*mv + dv; j++;
}
}
canvas.gfx.beginBitmapFill(drawTexture, null, repeat, smooth);
canvas.gfx.drawTriangles(vertices, indices, uvts, "none");
} else {
// Расчёт Z-баунда
var z:Number;
var min:Number = 1e+22;
var max:Number = -1;
for (face = list; face != null; face = face.processNext) {
for (wrapper = face.wrapper; wrapper != null; wrapper = wrapper.next) {
z = wrapper.vertex.cameraZ;
if (z < min) min = z;
if (z > max) max = z;
}
}
// Расстояние нулевого мипа
f = camera.focalLength*resolution;
// Минимальный и максимальный уровень
var minLevel:int = (min >= f) ? (1 + Math.log(min/f)*1.442695040888963387) : 0;
if (minLevel >= numMaps) minLevel = numMaps - 1;
var maxLevel:int = (max >= f) ? (1 + Math.log(max/f)*1.442695040888963387) : 0;
if (maxLevel >= numMaps) maxLevel = numMaps - 1;
// Рассечение Z-плоскостями начиная с дальних и отрисовка
z = f*Math.pow(2, maxLevel - 1);
var temporaryWrapper:Wrapper;
for (var i:int = maxLevel; i >= minLevel; i--) {
numDraws++;
numVertices = 0;
verticesLength = 0;
uvtsLength = 0;
indicesLength = 0;
var zMin:Number = z - threshold;
var zMax:Number = z + threshold;
for (face = list,list = null,last = null; face != null; face = next) {
next = face.processNext;
face.processNext = null;
wrapper = null;
if (i == minLevel) {
wrapper = face.wrapper;
} else {
var w:Wrapper = face.wrapper;
var az:Number = w.vertex.cameraZ;
w = w.next;
var bz:Number = w.vertex.cameraZ;
w = w.next;
var cz:Number = w.vertex.cameraZ;
w = w.next;
var behind:Boolean = az < zMin || bz < zMin || cz < zMin;
var infront:Boolean = az > zMax || bz > zMax || cz > zMax;
for (; w != null; w = w.next) {
var vz:Number = w.vertex.cameraZ;
if (vz < zMin) {
behind = true;
} else if (vz > zMax) {
infront = true;
}
}
if (!behind) {
wrapper = face.wrapper;
} else if (!infront) {
if (list != null) {
last.processNext = face;
} else {
list = face;
}
last = face;
} else {
var negative:Face = face.create();
camera.lastFace.next = negative;
camera.lastFace = negative;
var wNegative:Wrapper = null;
var wPositive:Wrapper = null;
var wNew:Wrapper;
//for (w = face.wrapper.next.next; w.next != null; w = w.next);
w = face.wrapper.next.next;
while (w.next != null) w = w.next;
var va:Vertex = w.vertex;
az = va.cameraZ;
for (w = face.wrapper; w != null; w = w.next) {
var vb:Vertex = w.vertex;
bz = vb.cameraZ;
if (az < zMin && bz > zMax || az > zMax && bz < zMin) {
t = (z - az)/(bz - az);
var v:Vertex = vb.create();
camera.lastVertex.next = v;
camera.lastVertex = v;
v.cameraX = va.cameraX + (vb.cameraX - va.cameraX)*t;
v.cameraY = va.cameraY + (vb.cameraY - va.cameraY)*t;
v.cameraZ = z;
v.u = va.u + (vb.u - va.u)*t;
v.v = va.v + (vb.v - va.v)*t;
wNew = w.create();
wNew.vertex = v;
if (wNegative != null) {
wNegative.next = wNew;
} else {
negative.wrapper = wNew;
}
wNegative = wNew;
wNew = w.create();
wNew.vertex = v;
if (wPositive != null) {
wPositive.next = wNew;
} else {
wrapper = wNew;
}
wPositive = wNew;
}
if (bz <= zMax) {
wNew = w.create();
wNew.vertex = vb;
if (wNegative != null) {
wNegative.next = wNew;
} else {
negative.wrapper = wNew;
}
wNegative = wNew;
}
if (bz >= zMin) {
wNew = w.create();
wNew.vertex = vb;
if (wPositive != null) {
wPositive.next = wNew;
} else {
wrapper = wNew;
}
wPositive = wNew;
}
va = vb;
az = bz;
}
if (list != null) {
last.processNext = negative;
} else {
list = negative;
}
last = negative;
temporaryWrapper = wrapper;
}
}
if (wrapper != null) {
vertex = wrapper.vertex;
if (vertex.drawID != numDraws) {
t = 1/vertex.cameraZ;
vertices[verticesLength] = vertex.cameraX*viewSizeX*t;
verticesLength++;
vertices[verticesLength] = vertex.cameraY*viewSizeY*t;
verticesLength++;
uvts[uvtsLength] = vertex.u;
uvtsLength++;
uvts[uvtsLength] = vertex.v;
uvtsLength++;
uvts[uvtsLength] = t;
uvtsLength++;
a = numVertices;
vertex.index = numVertices++;
vertex.drawID = numDraws;
} else {
a = vertex.index;
}
wrapper = wrapper.next;
vertex = wrapper.vertex;
if (vertex.drawID != numDraws) {
t = 1/vertex.cameraZ;
vertices[verticesLength] = vertex.cameraX*viewSizeX*t;
verticesLength++;
vertices[verticesLength] = vertex.cameraY*viewSizeY*t;
verticesLength++;
uvts[uvtsLength] = vertex.u;
uvtsLength++;
uvts[uvtsLength] = vertex.v;
uvtsLength++;
uvts[uvtsLength] = t;
uvtsLength++;
b = numVertices;
vertex.index = numVertices++;
vertex.drawID = numDraws;
} else {
b = vertex.index;
}
for (wrapper = wrapper.next; wrapper != null; wrapper = wrapper.next) {
vertex = wrapper.vertex;
if (vertex.drawID != numDraws) {
t = 1/vertex.cameraZ;
vertices[verticesLength] = vertex.cameraX*viewSizeX*t;
verticesLength++;
vertices[verticesLength] = vertex.cameraY*viewSizeY*t;
verticesLength++;
uvts[uvtsLength] = vertex.u;
uvtsLength++;
uvts[uvtsLength] = vertex.v;
uvtsLength++;
uvts[uvtsLength] = t;
uvtsLength++;
c = numVertices;
vertex.index = numVertices++;
vertex.drawID = numDraws;
} else {
c = vertex.index;
}
drawIndices[indicesLength] = a;
indicesLength++;
drawIndices[indicesLength] = b;
indicesLength++;
drawIndices[indicesLength] = c;
indicesLength++;
b = c;
numTriangles++;
}
numPolygons++;
if (temporaryWrapper != null) {
//for (wrapper = temporaryWrapper; wrapper != null; wrapper.vertex = null, wrapper = wrapper.next);
wrapper = temporaryWrapper;
while (wrapper != null) {
wrapper.vertex = null;
wrapper = wrapper.next;
}
camera.lastWrapper.next = temporaryWrapper;
camera.lastWrapper = wPositive;
temporaryWrapper = null;
}
}
}
// Следующая плоскость
z *= 0.5;
// Подрезка
vertices.length = verticesLength;
uvts.length = uvtsLength;
indices.length = indicesLength;
// Отрисовка
drawTexture = mipMap[i];
if (correctUV) {
du = -0.5/(drawTexture.width - 1);
dv = -0.5/(drawTexture.height - 1);
mu = 1 - du - du;
mv = 1 - dv - dv;
for (j = 0; j < uvtsLength; j++) {
uvts[j] = uvts[j]*mu + du; j++;
uvts[j] = uvts[j]*mv + dv; j++;
}
}
canvas.gfx.beginBitmapFill(drawTexture, null, repeat, smooth);
canvas.gfx.drawTriangles(vertices, indices, uvts, "none");
}
}
camera.numDraws = numDraws;
camera.numPolygons = numPolygons;
camera.numTriangles = numTriangles;
}
override alternativa3d function drawViewAligned(camera:Camera3D, canvas:Canvas, list:Face, distance:Number, a:Number, b:Number, c:Number, d:Number, tx:Number, ty:Number):void {
var viewSizeX:Number = camera.viewSizeX;
var viewSizeY:Number = camera.viewSizeY;
var face:Face;
var next:Face;
// Если нет текстуры, нужно просто расцепить список
if (_texture == null) {
clearLinks(list);
return;
}
var drawTexure:BitmapData;
if (_mipMapping == 0) {
// Без мипмаппинга
drawTexure = _texture;
} else {
// Мипмаппинг по удалённости объекта от камеры
var f:Number = camera.focalLength*resolution;
var level:int = (distance >= f) ? (1 + Math.log(distance/f)*1.442695040888963387) : 0;
if (level >= numMaps) level = numMaps - 1;
drawTexure = mipMap[level];
}
// Коррекция матрицы
var tw:Number = drawTexure.width;
var th:Number = drawTexure.height;
matrix.a = a/tw;
matrix.b = b/tw;
matrix.c = c/th;
matrix.d = d/th;
matrix.tx = tx;
matrix.ty = ty;
// Отрисовка
canvas.gfx.beginBitmapFill(drawTexure, matrix, repeat, smooth);
for (face = list; face != null; face = next) {
next = face.processNext;
face.processNext = null;
var wrapper:Wrapper = face.wrapper;
var vertex:Vertex = wrapper.vertex;
canvas.gfx.moveTo(vertex.cameraX*viewSizeX/distance, vertex.cameraY*viewSizeY/distance);
var numVertices:int = -1;
for (wrapper = wrapper.next; wrapper != null; wrapper = wrapper.next) {
vertex = wrapper.vertex;
canvas.gfx.lineTo(vertex.cameraX*viewSizeX/distance, vertex.cameraY*viewSizeY/distance);
numVertices++;
}
camera.numTriangles += numVertices;
camera.numPolygons++;
}
camera.numDraws++;
}
}
}

View File

@@ -0,0 +1,46 @@
package alternativa.engine3d.objects {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Canvas;
import alternativa.engine3d.core.Geometry;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.materials.Material;
use namespace alternativa3d;
/**
* Анимированный спрайт.
* Анимация осуществляется путём переключения изображений,
* хранящихся в списке textures
*/
public class AnimSprite extends Sprite3D {
/**
* Список кадров изображений
*/
public var materials:Vector.<Material>;
/**
* Устанавливаемый кадр
*/
public var frame:uint = 0;
public function AnimSprite(width:Number = 100, height:Number = 100, materials:Vector.<Material> = null) {
super(width, height);
this.materials = materials;
}
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
if (materials == null) return;
material = materials[frame];
super.draw(camera, object, parentCanvas);
}
override alternativa3d function getGeometry(camera:Camera3D, object:Object3D):Geometry {
if (materials == null) return null;
material = materials[frame];
return super.getGeometry(camera, object);
}
}
}

View File

@@ -0,0 +1,116 @@
package alternativa.engine3d.objects {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Canvas;
import alternativa.engine3d.core.Object3D;
use namespace alternativa3d;
/**
* Вспомогательный объект, иллюстрирующий систему координат
*/
public class Axes extends Object3D {
public var axisLength:Number;
public var lineThickness:Number;
public var dotRadius:Number;
public var textSize:Number;
public function Axes(axisLength:Number = 30, lineThickness:Number = 0, dotRadius:Number = 2, textSize:Number = 10):void {
this.axisLength = axisLength;
this.lineThickness = lineThickness;
this.dotRadius = dotRadius;
this.textSize = textSize;
boundMinX = -dotRadius;
boundMinY = -dotRadius;
boundMinZ = -dotRadius;
boundMaxX = dotRadius;
boundMaxY = dotRadius;
boundMaxZ = dotRadius;
}
/**
* @private
*/
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
/*
var p:Vector.<Number> = Vector.<Number>([0, 0, 0, axisLength, 0, 0, 0, axisLength, 0, 0, 0, axisLength]);
var d:Vector.<Number> = new Vector.<Number>(8);
object.transformation.transformVectors(p, p);
// Центр за камерой
if (p[2] < camera.nearClipping) return;
Utils3D.projectVectors(camera.projectionMatrix, p, d, new Vector.<Number>());
var size:Number = camera.viewSize/p[2];
// Подготовка канваса
var canvas:Canvas = parentCanvas.getChildCanvas(true, false, object.alpha, object.blendMode, object.colorTransform, object.filters);
var gfx:Graphics = canvas.gfx;
var text:TextField;
// Ось X
if (p[5] >= camera.nearClipping) {
gfx.lineStyle(lineThickness*size, 0xFF0000);
gfx.moveTo(d[0], d[1]);
gfx.lineTo(d[2], d[3]);
text = new TextField();
text.autoSize = TextFieldAutoSize.LEFT;
text.selectable = false;
text.x = d[2];
text.y = d[3];
text.text = "X";
text.setTextFormat(new TextFormat("Tahoma", textSize*size, 0xFF0000));
canvas.addChild(text);
canvas._numChildren++;
}
// Ось Y
if (p[8] >= camera.nearClipping) {
gfx.lineStyle(lineThickness*size, 0x00FF00);
gfx.moveTo(d[0], d[1]);
gfx.lineTo(d[4], d[5]);
text = new TextField();
text.autoSize = TextFieldAutoSize.LEFT;
text.selectable = false;
text.x = d[4];
text.y = d[5];
text.text = "Y";
text.setTextFormat(new TextFormat("Tahoma", textSize*size, 0x00FF00));
canvas.addChild(text);
canvas._numChildren++;
}
// Ось Z
if (p[11] >= camera.nearClipping) {
gfx.lineStyle(lineThickness*size, 0x0000FF);
gfx.moveTo(d[0], d[1]);
gfx.lineTo(d[6], d[7]);
text = new TextField();
text.autoSize = TextFieldAutoSize.LEFT;
text.selectable = false;
text.x = d[6];
text.y = d[7];
text.text = "Z";
text.setTextFormat(new TextFormat("Tahoma", textSize*size, 0x0000FF));
canvas.addChild(text);
canvas._numChildren++;
}
// Начало координат
gfx.lineStyle();
gfx.beginFill(0xFFFFFF);
gfx.drawCircle(d[0], d[1], dotRadius*size);
*/
//debugDrawBoundRaduis(camera, object, canvas);
}
}
}

View File

@@ -0,0 +1,57 @@
package alternativa.engine3d.objects {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Vertex;
use namespace alternativa3d;
public class Bone extends Joint {
public var length:Number;
public var distance:Number;
// Длина кости
private var lx:Number;
private var ly:Number;
private var lz:Number;
private var ldot:Number;
public function Bone(length:Number, distance:Number) {
this.length = length;
this.distance = distance;
}
override alternativa3d function calculateBindingMatrix(parent:Object3D):void {
super.calculateBindingMatrix(parent);
lx = mc*length;
ly = mg*length;
lz = mk*length;
ldot = lx*lx + ly*ly + lz*lz;
}
public function bindVerticesByDistance(skin:Skin):void {
for (var vertex:Vertex = skin.vertexList; vertex != null; vertex = vertex.next) bindVertexByDistance(vertex);
}
public function bindVertexByDistance(vertex:Vertex):void {
var vx:Number = vertex.x - md;
var vy:Number = vertex.y - mh;
var vz:Number = vertex.z - ml;
var dot:Number = vx*lx + vy*ly + vz*lz;
if (dot > 0) {
if (ldot > dot) {
dot /= ldot;
vx = vertex.x - md - dot*lx;
vy = vertex.y - mh - dot*ly;
vz = vertex.z - ml - dot*lz;
} else {
vx -= lx;
vy -= ly;
vz -= lz;
}
}
bindVertex(vertex, 1 - Math.sqrt(vx*vx + vy*vy + vz*vz)/distance);
}
}
}

View File

@@ -0,0 +1,215 @@
package alternativa.engine3d.objects {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Canvas;
import alternativa.engine3d.core.Debug;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Vertex;
import alternativa.engine3d.core.Geometry;
use namespace alternativa3d;
public class Joint extends Object3D {
private var joints:Vector.<Joint> = new Vector.<Joint>();
private var _numJoints:uint = 0;
alternativa3d var vertexBindingList:VertexBinding;
// Матрица привязки
private var ba:Number;
private var bb:Number;
private var bc:Number;
private var bd:Number;
private var be:Number;
private var bf:Number;
private var bg:Number;
private var bh:Number;
private var bi:Number;
private var bj:Number;
private var bk:Number;
private var bl:Number;
alternativa3d function calculateBindingMatrix(parent:Object3D):void {
composeAndAppend(parent);
calculateInverseMatrix(this);
ba = ima;
bb = imb;
bc = imc;
bd = imd;
be = ime;
bf = imf;
bg = img;
bh = imh;
bi = imi;
bj = imj;
bk = imk;
bl = iml;
for (var i:int = 0; i < _numJoints; i++) {
var joint:Joint = joints[i];
joint.calculateBindingMatrix(this);
}
}
/**
* @private
* Задает матрицу перевода из кости в скин.
*/
alternativa3d function setBindingMatrix(a:Number, b:Number, c:Number, d:Number, e:Number, f:Number, g:Number, h:Number, i:Number, j:Number, k:Number, l:Number):void {
ba = a;
bb = b;
bc = c;
bd = d;
be = e;
bf = f;
bg = g;
bh = h;
bi = i;
bj = j;
bk = k;
bl = l;
}
alternativa3d function addWeights():void {
for (var vertexBinding:VertexBinding = vertexBindingList; vertexBinding != null; vertexBinding = vertexBinding.next) vertexBinding.vertex.offset += vertexBinding.weight;
for (var i:int = 0; i < _numJoints; i++) {
var joint:Joint = joints[i];
joint.addWeights();
}
}
alternativa3d function normalizeWeights():void {
for (var vertexBinding:VertexBinding = vertexBindingList; vertexBinding != null; vertexBinding = vertexBinding.next) vertexBinding.weight /= vertexBinding.vertex.offset;
for (var i:int = 0; i < _numJoints; i++) {
var joint:Joint = joints[i];
joint.normalizeWeights();
}
}
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
var canvas:Canvas;
var debug:int;
composeAndAppend(object);
calculateVertices();
for (var i:int = 0; i < _numJoints; i++) {
var joint:Joint = joints[i];
joint.draw(camera, this, parentCanvas);
}
// Дебаг
if (camera.debug && (debug = camera.checkInDebug(this)) > 0) {
canvas = parentCanvas.getChildCanvas(object, true, false);
if (debug & Debug.BONES) drawBone(camera, canvas, 0xFFFFFF);
}
}
override alternativa3d function getGeometry(camera:Camera3D, object:Object3D):Geometry {
composeAndAppend(object);
calculateVertices();
for (var i:int = 0; i < _numJoints; i++) {
var joint:Joint = joints[i];
joint.getGeometry(camera, this);
}
return null;
}
private function drawBone(camera:Camera3D, canvas:Canvas, color:int):void {
for (var i:int = 0; i < _numJoints; i++) {
var x1:Number = 0;
var y1:Number = 0;
var z1:Number = 0;
var bone:Joint = joints[i];
var x2:Number = bone.x;
var y2:Number = bone.y;
var z2:Number = bone.z;
var cx1:Number = ma*x1 + mb*y1 + mc*z1 + md;
var cy1:Number = me*x1 + mf*y1 + mg*z1 + mh;
var cz1:Number = mi*x1 + mj*y1 + mk*z1 + ml;
var cx2:Number = ma*x2 + mb*y2 + mc*z2 + md;
var cy2:Number = me*x2 + mf*y2 + mg*z2 + mh;
var cz2:Number = mi*x2 + mj*y2 + mk*z2 + ml;
// Проецирование
var viewSizeX:Number = camera.viewSizeX;
var viewSizeY:Number = camera.viewSizeY;
var t1:Number = 1/cz1;
var t2:Number = 1/cz2;
var px1:Number = cx1*viewSizeX*t1;
var py1:Number = cy1*viewSizeY*t1;
var px2:Number = cx2*viewSizeX*t2;
var py2:Number = cy2*viewSizeY*t2;
canvas.gfx.lineStyle(0, color);
canvas.gfx.moveTo(px1, py1);
canvas.gfx.lineTo(px2, py2);
}
}
alternativa3d function calculateVertices():void {
// Матрица изменений координат в соответствии с изменением положения кости относительно слепка
ima = ma*ba + mb*be + mc*bi;
imb = ma*bb + mb*bf + mc*bj;
imc = ma*bc + mb*bg + mc*bk;
imd = ma*bd + mb*bh + mc*bl + md;
ime = me*ba + mf*be + mg*bi;
imf = me*bb + mf*bf + mg*bj;
img = me*bc + mf*bg + mg*bk;
imh = me*bd + mf*bh + mg*bl + mh;
imi = mi*ba + mj*be + mk*bi;
imj = mi*bb + mj*bf + mk*bj;
imk = mi*bc + mj*bg + mk*bk;
iml = mi*bd + mj*bh + mk*bl + ml;
// Расчёт координат
for (var vertexBinding:VertexBinding = vertexBindingList; vertexBinding != null; vertexBinding = vertexBinding.next) {
var vertex:Vertex = vertexBinding.vertex;
vertex.cameraX += (ima*vertex.x + imb*vertex.y + imc*vertex.z + imd)*vertexBinding.weight;
vertex.cameraY += (ime*vertex.x + imf*vertex.y + img*vertex.z + imh)*vertexBinding.weight;
vertex.cameraZ += (imi*vertex.x + imj*vertex.y + imk*vertex.z + iml)*vertexBinding.weight;
}
}
public function bindVertex(vertex:Vertex, weight:Number = 1):void {
if (weight > 0) {
var vertexBinding:VertexBinding = new VertexBinding();
vertexBinding.next = vertexBindingList;
vertexBindingList = vertexBinding;
vertexBinding.vertex = vertex;
vertexBinding.weight = weight;
}
}
public function addJoint(joint:Joint):void {
joints[_numJoints] = joint;
_numJoints++;
}
public function removeJoint(joint:Joint):void {
var i:int = joints.indexOf(joint);
if (i < 0) throw new ArgumentError("Joint not found");
_numJoints--;
var j:int = i + 1;
while (i < _numJoints) {
joints[i] = joints[j];
i++;
j++;
}
joints.length = _numJoints;
}
public function get numJoints():uint {
return _numJoints;
}
public function getJointAt(index:uint):Joint {
return joints[index];
}
alternativa3d override function updateBounds(bounds:Object3D, transformation:Object3D = null):void {
composeAndAppend(transformation);
calculateVertices();
for (var i:int = 0; i < _numJoints; i++) {
var joint:Joint = joints[i];
joint.updateBounds(bounds, this);
}
}
}
}

View File

@@ -0,0 +1,71 @@
package alternativa.engine3d.objects {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Canvas;
import alternativa.engine3d.core.Geometry;
import alternativa.engine3d.core.Object3D;
use namespace alternativa3d;
/**
* Объект, имеющий набор объектов с разной детализацией.
* При отрисовке, он выбирает в зависимости от расстояния от камеры
* объект с нужной детализацией и отрисовывает его вместо себя.
* Это позволяет получить лучший визуальный результат и большую производительность.
*/
public class LOD extends Object3D {
/**
* Объекты с разной детализацией
*/
public var lodObjects:Vector.<Object3D>;
/**
* Расстояния до камеры соответствующие объектам с разной детализацией
*/
public var lodDistances:Vector.<Number>;
/**
* @private
*/
private function getLODObject(object:Object3D):Object3D {
//var cameraDistance:Number = object.cameraMatrix.position.length;
var cameraDistance:Number = object.ml;
// Поиск ближайшего лода
var min:Number = Infinity;
var length:uint = lodObjects.length;
var lod:Object3D;
for (var i:int = 0; i < length; i++) {
var d:Number = Math.abs(cameraDistance - lodDistances[i]);
if (d < min) {
min = d;
lod = lodObjects[i];
}
}
return lod;
}
/**
* @private
*/
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
getLODObject(object).draw(camera, object, parentCanvas);
}
override alternativa3d function getGeometry(camera:Camera3D, object:Object3D):Geometry {
return getLODObject(object).getGeometry(camera, object);
}
override alternativa3d function updateBounds(bounds:Object3D, transformation:Object3D = null):void {
var length:uint = lodObjects.length;
for (var i:int = 0; i < length; i++) {
(lodObjects[i] as Object3D).updateBounds(bounds, transformation);
}
}
override alternativa3d function cullingInCamera(camera:Camera3D, object:Object3D, culling:int):int {
object.culling = getLODObject(object).cullingInCamera(camera, object, culling);
return object.culling;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,499 @@
package alternativa.engine3d.objects {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Canvas;
import alternativa.engine3d.core.Debug;
import alternativa.engine3d.core.Face;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Vertex;
import alternativa.engine3d.core.Wrapper;
use namespace alternativa3d;
/**
* Полигональный объект-перекрытие.
* Объекты, которые он перекрывает от видимости камеры, исключаются из отрисовки.
* Сам окклюдер не отрисовывается.
* Должен быть конвексным
*/
public class Occluder extends Object3D {
public var faceList:Face;
public var edgeList:Edge;
public var vertexList:Vertex;
/**
* Минимальное отношение площади перекрытия окклюдером вьюпорта к площади вьюпорта (от 0 до 1)
* Если окклюдер перекрывает больше, он помещается в очередь и учитывается
* при дальнейшей отрисовке в пределах кадра, иначе игнорируется
*/
public var minSize:Number = 0;
/**
* Копирование геометрии меша
* @param source Объект копирования
* Меш, геометрия которого копируется, обязан быть конвексным, иначе окклюдер будет некорректно работать
*/
public function copyFrom(source:Mesh):void {
x = source.x;
y = source.y;
z = source.z;
rotationX = source.rotationX;
rotationY = source.rotationY;
rotationZ = source.rotationZ;
scaleX = source.scaleX;
scaleY = source.scaleY;
scaleZ = source.scaleZ;
boundMinX = source.boundMinX;
boundMinY = source.boundMinY;
boundMinZ = source.boundMinZ;
boundMaxX = source.boundMaxX;
boundMaxY = source.boundMaxY;
boundMaxZ = source.boundMaxZ;
faceList = source.faceList;
vertexList = source.vertexList;
}
override alternativa3d function updateBounds(bounds:Object3D, transformation:Object3D = null):void {
for (var vertex:Vertex = vertexList; vertex != null; vertex = vertex.next) {
if (transformation != null) {
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;
} else {
vertex.cameraX = vertex.x;
vertex.cameraY = vertex.y;
vertex.cameraZ = vertex.z;
}
if (vertex.cameraX < bounds.boundMinX) bounds.boundMinX = vertex.cameraX;
if (vertex.cameraX > bounds.boundMaxX) bounds.boundMaxX = vertex.cameraX;
if (vertex.cameraY < bounds.boundMinY) bounds.boundMinY = vertex.cameraY;
if (vertex.cameraY > bounds.boundMaxY) bounds.boundMaxY = vertex.cameraY;
if (vertex.cameraZ < bounds.boundMinZ) bounds.boundMinZ = vertex.cameraZ;
if (vertex.cameraZ > bounds.boundMaxZ) bounds.boundMaxZ = vertex.cameraZ;
}
}
/**
* Расчёт рёбер по имеющимся вершинам и граням
*/
public function calculateEdges():void {
var face:Face;
var wrapper:Wrapper;
var edge:Edge;
// Построение рёбер
edgeList = null;
for (face = faceList; face != null; face = face.next) {
// Расчёт нормали
face.calculateBestSequenceAndNormal();
// Перебор отрезков грани
var a:Vertex;
var b:Vertex;
for (wrapper = face.wrapper; wrapper != null; wrapper = wrapper.next,a = b) {
a = wrapper.vertex;
b = (wrapper.next != null) ? wrapper.next.vertex : face.wrapper.vertex;
// Перебор созданных рёбер
for (edge = edgeList; edge != null; edge = edge.next) {
// Если некорректная геометрия
if (edge.a == a && edge.b == b) {
trace("Incorrect face joint");
}
// Если найдено созданное ребро с такими вершинами
if (edge.a == b && edge.b == a) break;
}
if (edge != null) {
edge.right = face;
} else {
edge = new Edge();
edge.a = a;
edge.b = b;
edge.left = face;
edge.next = edgeList;
edgeList = edge;
}
}
}
// Проверка на валидность
for (edge = edgeList; edge != null; edge = edge.next) {
var abx:Number = edge.b.x - edge.a.x;
var aby:Number = edge.b.y - edge.a.y;
var abz:Number = edge.b.z - edge.a.z;
var crx:Number = edge.right.normalZ*edge.left.normalY - edge.right.normalY*edge.left.normalZ;
var cry:Number = edge.right.normalX*edge.left.normalZ - edge.right.normalZ*edge.left.normalX;
var crz:Number = edge.right.normalY*edge.left.normalX - edge.right.normalX*edge.left.normalY;
// Если перегиб внутрь
if (abx*crx + aby*cry + abz*crz < 0) {
trace("Geometry is non convex");
}
// Если ребро с одной гранью
if (edge.left == null || edge.right == null) {
trace("Geometry is non whole");
}
}
}
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
if (faceList == null || edgeList == null) return;
var canvas:Canvas;
var debug:int;
// Расчёт инверсной матрицы камеры
calculateInverseMatrix(object);
// Определение видимости граней
var cameraInside:Boolean = true;
for (var face:Face = faceList; face != null; face = face.next) {
if (face.normalX*imd + face.normalY*imh + face.normalZ*iml > face.offset) {
face.distance = 1;
cameraInside = false;
} else {
face.distance = 0;
}
}
if (cameraInside) return;
// Подготовка окклюдера в камере
var occluder:Vertex;
var num:int = 0;
var occludeAll:Boolean = true;
var culling:int = object.culling;
var viewSizeX:Number = camera.viewSizeX;
var viewSizeY:Number = camera.viewSizeY;
var a:Vertex;
var b:Vertex;
var ax:Number;
var ay:Number;
var az:Number;
var bx:Number;
var by:Number;
var bz:Number;
var t:Number;
// Расчёт контура
for (var edge:Edge = edgeList; edge != null; edge = edge.next) {
// Если ребро в описывающем контуре
if (edge.left.distance != edge.right.distance) {
// Определение направления (против часовой)
if (edge.left.distance > 0) {
a = edge.a;
b = edge.b;
} else {
a = edge.b;
b = edge.a;
}
// Трансформация в камеру
ax = object.ma*a.x + object.mb*a.y + object.mc*a.z + object.md;
ay = object.me*a.x + object.mf*a.y + object.mg*a.z + object.mh;
az = object.mi*a.x + object.mj*a.y + object.mk*a.z + object.ml;
bx = object.ma*b.x + object.mb*b.y + object.mc*b.z + object.md;
by = object.me*b.x + object.mf*b.y + object.mg*b.z + object.mh;
bz = object.mi*b.x + object.mj*b.y + object.mk*b.z + object.ml;
// Клиппинг
if (culling > 0) {
if (az <= -ax && bz <= -bx) {
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
continue;
} else if (bz > -bx && az <= -ax) {
t = (ax + az)/(ax + az - bx - bz);
ax += (bx - ax)*t;
ay += (by - ay)*t;
az += (bz - az)*t;
} else if (bz <= -bx && az > -ax) {
t = (ax + az)/(ax + az - bx - bz);
bx = ax + (bx - ax)*t;
by = ay + (by - ay)*t;
bz = az + (bz - az)*t;
}
if (az <= ax && bz <= bx) {
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
continue;
} else if (bz > bx && az <= ax) {
t = (az - ax)/(az - ax + bx - bz);
ax += (bx - ax)*t;
ay += (by - ay)*t;
az += (bz - az)*t;
} else if (bz <= bx && az > ax) {
t = (az - ax)/(az - ax + bx - bz);
bx = ax + (bx - ax)*t;
by = ay + (by - ay)*t;
bz = az + (bz - az)*t;
}
if (az <= -ay && bz <= -by) {
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
continue;
} else if (bz > -by && az <= -ay) {
t = (ay + az)/(ay + az - by - bz);
ax += (bx - ax)*t;
ay += (by - ay)*t;
az += (bz - az)*t;
} else if (bz <= -by && az > -ay) {
t = (ay + az)/(ay + az - by - bz);
bx = ax + (bx - ax)*t;
by = ay + (by - ay)*t;
bz = az + (bz - az)*t;
}
if (az <= ay && bz <= by) {
if (occludeAll && by*ax - bx*ay > 0) occludeAll = false;
continue;
} else if (bz > by && az <= ay) {
t = (az - ay)/(az - ay + by - bz);
ax += (bx - ax)*t;
ay += (by - ay)*t;
az += (bz - az)*t;
} else if (bz <= by && az > ay) {
t = (az - ay)/(az - ay + by - bz);
bx = ax + (bx - ax)*t;
by = ay + (by - ay)*t;
bz = az + (bz - az)*t;
}
occludeAll = false;
}
// Создание новой плоскости
a = a.create();
a.next = occluder;
num++;
occluder = a;
// Плоскость
occluder.cameraX = bz*ay - by*az;
occluder.cameraY = bx*az - bz*ax;
occluder.cameraZ = by*ax - bx*ay;
// Ребро (для перевода в систему контейнера)
occluder.x = ax;
occluder.y = ay;
occluder.z = az;
occluder.u = bx;
occluder.v = by;
occluder.offset = bz;
}
}
// Если контур не нулевой
if (occluder != null) {
// Проверка размера на экране
if (minSize > 0) {
// Проецирование рёбер контура
var projected:Vertex = Vertex.createList(num);
for (a = occluder,b = projected; a != null; a = a.next,b = b.next) {
// Проецтрование
b.x = a.x*viewSizeX/a.z;
b.y = a.y*viewSizeY/a.z;
b.u = a.u*viewSizeX/a.offset;
b.v = a.v*viewSizeY/a.offset;
// Расчёт левой нормали
b.cameraX = b.y - b.v;
b.cameraY = b.u - b.x;
b.offset = b.cameraX*b.x + b.cameraY*b.y;
}
// Клиппинг рамки вьюпорта по рёбрам контура
var frame:Vertex;
if (culling > 0) {
if (culling & 4) {
ax = -camera.viewSizeX;
ay = -camera.viewSizeY;
bx = -camera.viewSizeX;
by = camera.viewSizeY;
for (a = projected; a != null; a = a.next) {
az = ax*a.cameraX + ay*a.cameraY - a.offset;
bz = bx*a.cameraX + by*a.cameraY - a.offset;
if (az < 0 || bz < 0) {
if (az >= 0 && bz < 0) {
t = az/(az - bz);
ax += (bx - ax)*t;
ay += (by - ay)*t;
} else if (az < 0 && bz >= 0) {
t = az/(az - bz);
bx = ax + (bx - ax)*t;
by = ay + (by - ay)*t;
}
} else break;
}
if (a == null) {
b = occluder.create();
b.next = frame;
frame = b;
frame.x = ax;
frame.y = ay;
frame.u = bx;
frame.v = by;
}
}
if (culling & 8) {
ax = camera.viewSizeX;
ay = camera.viewSizeY;
bx = camera.viewSizeX;
by = -camera.viewSizeY;
for (a = projected; a != null; a = a.next) {
az = ax*a.cameraX + ay*a.cameraY - a.offset;
bz = bx*a.cameraX + by*a.cameraY - a.offset;
if (az < 0 || bz < 0) {
if (az >= 0 && bz < 0) {
t = az/(az - bz);
ax += (bx - ax)*t;
ay += (by - ay)*t;
} else if (az < 0 && bz >= 0) {
t = az/(az - bz);
bx = ax + (bx - ax)*t;
by = ay + (by - ay)*t;
}
} else break;
}
if (a == null) {
b = occluder.create();
b.next = frame;
frame = b;
frame.x = ax;
frame.y = ay;
frame.u = bx;
frame.v = by;
}
}
if (culling & 16) {
ax = camera.viewSizeX;
ay = -camera.viewSizeY;
bx = -camera.viewSizeX;
by = -camera.viewSizeY;
for (a = projected; a != null; a = a.next) {
az = ax*a.cameraX + ay*a.cameraY - a.offset;
bz = bx*a.cameraX + by*a.cameraY - a.offset;
if (az < 0 || bz < 0) {
if (az >= 0 && bz < 0) {
t = az/(az - bz);
ax += (bx - ax)*t;
ay += (by - ay)*t;
} else if (az < 0 && bz >= 0) {
t = az/(az - bz);
bx = ax + (bx - ax)*t;
by = ay + (by - ay)*t;
}
} else break;
}
if (a == null) {
b = occluder.create();
b.next = frame;
frame = b;
frame.x = ax;
frame.y = ay;
frame.u = bx;
frame.v = by;
}
}
if (culling & 32) {
ax = -camera.viewSizeX;
ay = camera.viewSizeY;
bx = camera.viewSizeX;
by = camera.viewSizeY;
for (a = projected; a != null; a = a.next) {
az = ax*a.cameraX + ay*a.cameraY - a.offset;
bz = bx*a.cameraX + by*a.cameraY - a.offset;
if (az < 0 || bz < 0) {
if (az >= 0 && bz < 0) {
t = az/(az - bz);
ax += (bx - ax)*t;
ay += (by - ay)*t;
} else if (az < 0 && bz >= 0) {
t = az/(az - bz);
bx = ax + (bx - ax)*t;
by = ay + (by - ay)*t;
}
} else break;
}
if (a == null) {
b = occluder.create();
b.next = frame;
frame = b;
frame.x = ax;
frame.y = ay;
frame.u = bx;
frame.v = by;
}
}
}
// Нахождение площади перекрытия
var square:Number = 0;
az = projected.x;
bz = projected.y;
a = projected;
while (a.next != null) a = a.next;
for (a.next = frame,a = projected; a != null; a = a.next) {
square += (a.u - az)*(a.y - bz) - (a.v - bz)*(a.x - az);
if (a.next == null) break;
}
// Зачистка
a.next = Vertex.collector;
Vertex.collector = projected;
// Если площадь меньше заданной
if (square/(camera.viewSizeX*camera.viewSizeY*8) < minSize) {
// Зачистка
a = occluder;
while (a.next != null) a = a.next;
a.next = Vertex.collector;
Vertex.collector = occluder;
return;
}
}
// Дебаг
if (camera.debug && (debug = camera.checkInDebug(this)) > 0) {
canvas = parentCanvas.getChildCanvas(object, true, false);
if (debug & Debug.EDGES) {
for (a = occluder; a != null; a = a.next) {
ax = a.x*viewSizeX/a.z;
ay = a.y*viewSizeY/a.z;
bx = a.u*viewSizeX/a.offset;
by = a.v*viewSizeY/a.offset;
canvas.gfx.moveTo(ax, ay);
canvas.gfx.lineStyle(3, 0x0000FF);
canvas.gfx.lineTo(ax + (bx - ax)*0.8, ay + (by - ay)*0.8);
canvas.gfx.lineStyle(3, 0xFF0000);
canvas.gfx.lineTo(bx, by);
}
}
if (debug & Debug.BOUNDS) Debug.drawBounds(camera, canvas, object, boundMinX, boundMinY, boundMinZ, boundMaxX, boundMaxY, boundMaxZ);
}
// Добавление окклюдера в камеру
camera.occluders[camera.numOccluders] = occluder;
camera.numOccluders++;
// Если окклюдер перекрывает весь экран
} else if (occludeAll) {
// Дебаг
if (camera.debug && (debug = camera.checkInDebug(this)) > 0) {
canvas = parentCanvas.getChildCanvas(object, true, false);
if (debug & Debug.EDGES) {
t = 1.5;
canvas.gfx.moveTo(-viewSizeX + t, -viewSizeY + t);
canvas.gfx.lineStyle(3, 0x0000FF);
canvas.gfx.lineTo(-viewSizeX + t, viewSizeY*0.6);
canvas.gfx.lineStyle(3, 0xFF0000);
canvas.gfx.lineTo(-viewSizeX + t, viewSizeY - t);
canvas.gfx.lineStyle(3, 0x0000FF);
canvas.gfx.lineTo(viewSizeX*0.6, viewSizeY - t);
canvas.gfx.lineStyle(3, 0xFF0000);
canvas.gfx.lineTo(viewSizeX - t, viewSizeY - t);
canvas.gfx.lineStyle(3, 0x0000FF);
canvas.gfx.lineTo(viewSizeX - t, -viewSizeY*0.6);
canvas.gfx.lineStyle(3, 0xFF0000);
canvas.gfx.lineTo(viewSizeX - t, -viewSizeY + t);
canvas.gfx.lineStyle(3, 0x0000FF);
canvas.gfx.lineTo(-viewSizeX*0.6, -viewSizeY + t);
canvas.gfx.lineStyle(3, 0xFF0000);
canvas.gfx.lineTo(-viewSizeX + t, -viewSizeY + t);
}
if (debug & Debug.BOUNDS) Debug.drawBounds(camera, canvas, object, boundMinX, boundMinY, boundMinZ, boundMaxX, boundMaxY, boundMaxZ);
}
camera.clearOccluders();
camera.occludedAll = true;
}
}
}
}
import alternativa.engine3d.core.Face;
import alternativa.engine3d.core.Vertex;
class Edge {
public var next:Edge;
public var a:Vertex;
public var b:Vertex;
public var left:Face;
public var right:Face;
}

View File

@@ -0,0 +1,45 @@
package alternativa.engine3d.objects {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Canvas;
import alternativa.engine3d.core.Geometry;
import alternativa.engine3d.core.Object3D;
use namespace alternativa3d;
/**
* Объект-ссылка.
* Может ссылаться на любой трёхмерный объект, в том числе контейнер с любой вложенностью или Reference.
* При отрисовке он отрисовывает вместо себя объект,
* на который ссылается, подставляя только свою трансформацию, alpha, blendMode, colorTransform и filters.
*/
public class Reference extends Object3D {
/**
* Объект, который подставляется при отрисовке вместо себя
*/
public var referenceObject:Object3D;
public function Reference(referenceObject:Object3D = null) {
this.referenceObject = referenceObject;
}
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
referenceObject.draw(camera, object, parentCanvas);
}
override alternativa3d function getGeometry(camera:Camera3D, object:Object3D):Geometry {
return referenceObject.getGeometry(camera, object);
}
override alternativa3d function updateBounds(bounds:Object3D, transformation:Object3D = null):void {
referenceObject.updateBounds(bounds, transformation);
}
override alternativa3d function cullingInCamera(camera:Camera3D, object:Object3D, culling:int):int {
object.culling = referenceObject.cullingInCamera(camera, object, culling);
return object.culling;
}
}
}

View File

@@ -0,0 +1,835 @@
package alternativa.engine3d.objects {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Canvas;
import alternativa.engine3d.core.Debug;
import alternativa.engine3d.core.Face;
import alternativa.engine3d.core.Geometry;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Vertex;
import alternativa.engine3d.core.Wrapper;
use namespace alternativa3d;
public class Skin extends Mesh {
private var joints:Vector.<Joint> = new Vector.<Joint>();
private var _numJoints:uint = 0;
public function calculateBindingMatrices():void {
ma = 1;
mb = 0;
mc = 0;
md = 0;
me = 0;
mf = 1;
mg = 0;
mh = 0;
mi = 0;
mj = 0;
mk = 1;
ml = 0;
for (var i:int = 0; i < _numJoints; i++) {
var joint:Joint = joints[i];
joint.calculateBindingMatrix(this);
}
}
public function normalizeWeights():void {
for (var vertex:Vertex = vertexList; vertex != null; vertex = vertex.next) vertex.offset = 0;
var joint:Joint;
for (var i:int = 0; i < _numJoints; i++) {
joint = joints[i];
joint.addWeights();
}
for (i = 0; i < _numJoints; i++) {
joint = joints[i];
joint.normalizeWeights();
}
}
public function addJoint(joint:Joint):void {
joints[_numJoints++] = joint;
}
public function removeJoint(joint:Joint):void {
var i:int = joints.indexOf(joint);
if (i < 0) throw new ArgumentError("Joint not found");
_numJoints--;
var j:int = i + 1;
while (i < _numJoints) {
joints[i] = joints[j];
i++;
j++;
}
joints.length = _numJoints;
}
public function get numJoints():uint {
return _numJoints;
}
public function getJointAt(index:uint):Joint {
return joints[index];
}
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
var canvas:Canvas;
var debug:int;
var list:Face;
var vertex:Vertex;
// Коррекция куллинга
var culling:int = object.culling;
if (clipping == 0) {
if (culling & 1) return;
culling = 0;
}
// Обнуление вершин
for (vertex = vertexList; vertex != null; vertex = vertex.next) {
vertex.cameraX = 0;
vertex.cameraY = 0;
vertex.cameraZ = 0;
vertex.drawID = 0;
}
// Расчёт координат вершин
for (var i:int = 0; i < _numJoints; i++) {
var joint:Joint = joints[i];
joint.draw(camera, object, parentCanvas);
}
// Отсечение по нормалям
var last:Face;
for (var face:Face = faceList; face != null; face = face.next) {
var a:Vertex = face.wrapper.vertex;
var b:Vertex = face.wrapper.next.vertex;
var c:Vertex = face.wrapper.next.next.vertex;
var abx:Number = b.cameraX - a.cameraX;
var aby:Number = b.cameraY - a.cameraY;
var abz:Number = b.cameraZ - a.cameraZ;
var acx:Number = c.cameraX - a.cameraX;
var acy:Number = c.cameraY - a.cameraY;
var acz:Number = c.cameraZ - a.cameraZ;
if ((acz*aby - acy*abz)*a.cameraX + (acx*abz - acz*abx)*a.cameraY + (acy*abx - acx*aby)*a.cameraZ < 0) {
if (list != null) {
last.processNext = face;
} else {
list = face;
}
last = face;
}
}
if (last != null) {
last.processNext = null;
}
if (list == null) return;
// Отсечение по пирамиде видимости
if (culling > 0) {
if (clipping == 1) {
list = cull(list, culling, camera);
} else {
list = clip(list, culling, camera);
}
if (list == null) return;
}
// Сортировка
if (list.processNext != null) {
if (sorting == 1) {
list = sortByAverageZ(list);
} else if (sorting == 2) {
list = sortByDynamicBSP(list, camera, threshold);
}
}
// Дебаг
if (camera.debug && (debug = camera.checkInDebug(this)) > 0) {
canvas = parentCanvas.getChildCanvas(object, true, false);
if (debug & Debug.EDGES) Debug.drawEdges(camera, canvas, list, 0xFFFFFF);
if (debug & Debug.BOUNDS) Debug.drawBounds(camera, canvas, object, boundMinX, boundMinY, boundMinZ, boundMaxX, boundMaxY, boundMaxZ);
}
// Отрисовка
canvas = parentCanvas.getChildCanvas(object, true, false, object.alpha, object.blendMode, object.colorTransform, object.filters);
for (face = list; face != null; face = next) {
var next:Face = face.processNext;
// Если конец списка или смена материала
if (next == null || next.material != list.material) {
// Разрыв на стыке разных материалов
face.processNext = null;
// Если материал для части списка не пустой
if (list.material != null) {
// Отрисовка
list.material.draw(camera, canvas, list, object.ml);
} else {
// Разрыв связей
while (list != null) {
face = list.processNext;
list.processNext = null;
list = face;
}
}
list = next;
}
}
}
override alternativa3d function updateBounds(bounds:Object3D, transformation:Object3D = null):void {
// Обнуление вершин
for (vertex = vertexList; vertex != null; vertex = vertex.next) {
vertex.cameraX = 0;
vertex.cameraY = 0;
vertex.cameraZ = 0;
}
// Расчёт координат вершин
if (transformation == null) {
ma = 1;
mb = 0;
mc = 0;
md = 0;
me = 0;
mf = 1;
mg = 0;
mh = 0;
mi = 0;
mj = 0;
mk = 1;
ml = 0;
transformation = this;
}
for (var i:int = 0; i < _numJoints; i++) {
var joint:Joint = joints[i];
joint.updateBounds(bounds, transformation);
}
// Расширение баунда
for (var vertex:Vertex = vertexList; vertex != null; vertex = vertex.next) {
if (vertex.cameraX < bounds.boundMinX) bounds.boundMinX = vertex.cameraX;
if (vertex.cameraX > bounds.boundMaxX) bounds.boundMaxX = vertex.cameraX;
if (vertex.cameraY < bounds.boundMinY) bounds.boundMinY = vertex.cameraY;
if (vertex.cameraY > bounds.boundMaxY) bounds.boundMaxY = vertex.cameraY;
if (vertex.cameraZ < bounds.boundMinZ) bounds.boundMinZ = vertex.cameraZ;
if (vertex.cameraZ > bounds.boundMaxZ) bounds.boundMaxZ = vertex.cameraZ;
}
}
override alternativa3d function getGeometry(camera:Camera3D, object:Object3D):Geometry {
var vertex:Vertex;
// Коррекция куллинга
var culling:int = object.culling;
if (clipping == 0) {
if (culling & 1) return null;
culling = 0;
}
// Расчёт инверсной матрицы камеры
calculateInverseMatrix(object);
// Обнуление вершин
for (vertex = vertexList; vertex != null; vertex = vertex.next) {
vertex.cameraX = 0;
vertex.cameraY = 0;
vertex.cameraZ = 0;
vertex.transformID = 1;
//trace(vertex.transformID);
vertex.drawID = 0;
}
// Расчёт координат вершин
for (var i:int = 0; i < _numJoints; i++) {
var joint:Joint = joints[i];
joint.getGeometry(camera, object);
}
// Получение клона видимой геометрии
var struct:Face;
if (sorting == 3) {
return null;
} else {
if (faceList == null) return null;
struct = calculateFaces(faceList, culling, camera, object.ma, object.mb, object.mc, object.md, object.me, object.mf, object.mg, object.mh, object.mi, object.mj, object.mk, object.ml);
}
// Зачистка после ремапа
for (vertex = vertexList; vertex != null; vertex = vertex.next) {
vertex.value = null;
}
// Создание геометрии
if (struct != null) {
var geometry:Geometry = Geometry.create();
geometry.interactiveObject = object;
geometry.faceStruct = struct;
geometry.ma = object.ma;
geometry.mb = object.mb;
geometry.mc = object.mc;
geometry.md = object.md;
geometry.me = object.me;
geometry.mf = object.mf;
geometry.mg = object.mg;
geometry.mh = object.mh;
geometry.mi = object.mi;
geometry.mj = object.mj;
geometry.mk = object.mk;
geometry.ml = object.ml;
geometry.ima = ima;
geometry.imb = imb;
geometry.imc = imc;
geometry.imd = imd;
geometry.ime = ime;
geometry.imf = imf;
geometry.img = img;
geometry.imh = imh;
geometry.imi = imi;
geometry.imj = imj;
geometry.imk = imk;
geometry.iml = iml;
geometry.alpha = object.alpha;
geometry.blendMode = object.blendMode;
geometry.colorTransform = object.colorTransform;
geometry.filters = object.filters;
geometry.sorting = sorting;
if (camera.debug) geometry.debug = camera.checkInDebug(this);
return geometry;
} else {
return null;
}
}
override protected function calculateFaces(struct:Face, culling:int, camera:Camera3D, ma:Number, mb:Number, mc:Number, md:Number, me:Number, mf:Number, mg:Number, mh:Number, mi:Number, mj:Number, mk:Number, ml:Number):Face {
var first:Face;
var last:Face;
var a:Vertex;
var b:Vertex;
var c:Vertex;
var d:Wrapper;
var v:Vertex;
var w:Wrapper;
var wFirst:Wrapper;
var wLast:Wrapper;
var wNext:Wrapper;
var wNew:Wrapper;
var ax:Number;
var ay:Number;
var az:Number;
var bx:Number;
var by:Number;
var bz:Number;
var cx:Number;
var cy:Number;
var cz:Number;
var c1:Boolean = (culling & 1) > 0;
var c2:Boolean = (culling & 2) > 0;
var c4:Boolean = (culling & 4) > 0;
var c8:Boolean = (culling & 8) > 0;
var c16:Boolean = (culling & 16) > 0;
var c32:Boolean = (culling & 32) > 0;
var near:Number = camera.nearClipping;
var far:Number = camera.farClipping;
var needX:Boolean = c4 || c8;
var needY:Boolean = c16 || c32;
var t:Number;
// Перебор оригинальных граней
for (var face:Face = struct; face != null; face = face.next) {
// Отсечение по нормали
d = face.wrapper;
a = d.vertex;
d = d.next;
b = d.vertex;
d = d.next;
c = d.vertex;
d = d.next;
var abx:Number = b.cameraX - a.cameraX;
var aby:Number = b.cameraY - a.cameraY;
var abz:Number = b.cameraZ - a.cameraZ;
var acx:Number = c.cameraX - a.cameraX;
var acy:Number = c.cameraY - a.cameraY;
var acz:Number = c.cameraZ - a.cameraZ;
if ((acz*aby - acy*abz)*a.cameraX + (acx*abz - acz*abx)*a.cameraY + (acy*abx - acx*aby)*a.cameraZ >= 0) continue;
var faceCulling:int = 0;
// Отсечение по пирамиде видимости
if (culling > 0) {
if (needX) {
ax = a.cameraX;
bx = b.cameraX;
cx = c.cameraX;
}
if (needY) {
ay = a.cameraY;
by = b.cameraY;
cy = c.cameraY;
}
az = a.cameraZ;
bz = b.cameraZ;
cz = c.cameraZ;
// Куллинг
if (clipping == 1) {
if (c1) {
if (az <= near || bz <= near || cz <= near) continue;
for (w = d; w != null; w = w.next) {
if (w.vertex.cameraZ <= near) break;
}
if (w != null) continue;
}
if (c2 && az >= far && bz >= far && cz >= far) {
for (w = d; w != null; w = w.next) {
if (w.vertex.cameraZ < far) break;
}
if (w == null) continue;
}
if (c4 && az <= -ax && bz <= -bx && cz <= -cx) {
for (w = d; w != null; w = w.next) {
v = w.vertex;
if (-v.cameraX < v.cameraZ) break;
}
if (w == null) continue;
}
if (c8 && az <= ax && bz <= bx && cz <= cx) {
for (w = d; w != null; w = w.next) {
v = w.vertex;
if (v.cameraX < v.cameraZ) break;
}
if (w == null) continue;
}
if (c16 && az <= -ay && bz <= -by && cz <= -cy) {
for (w = d; w != null; w = w.next) {
v = w.vertex;
if (-v.cameraY < v.cameraZ) break;
}
if (w == null) continue;
}
if (c32 && az <= ay && bz <= by && cz <= cy) {
for (w = d; w != null; w = w.next) {
v = w.vertex;
if (v.cameraY < v.cameraZ) break;
}
if (w == null) continue;
}
// Клиппинг
} else {
if (c1) {
if (az <= near && bz <= near && cz <= near) {
for (w = d; w != null; w = w.next) {
if (w.vertex.cameraZ > near) {
faceCulling |= 1;
break;
}
}
if (w == null) continue;
} else if (az > near && bz > near && cz > near) {
for (w = d; w != null; w = w.next) {
if (w.vertex.cameraZ <= near) {
faceCulling |= 1;
break;
}
}
} else {
faceCulling |= 1;
}
}
if (c2) {
if (az >= far && bz >= far && cz >= far) {
for (w = d; w != null; w = w.next) {
if (w.vertex.cameraZ < far) {
faceCulling |= 2;
break;
}
}
if (w == null) continue;
} else if (az < far && bz < far && cz < far) {
for (w = d; w != null; w = w.next) {
if (w.vertex.cameraZ >= far) {
faceCulling |= 2;
break;
}
}
} else {
faceCulling |= 2;
}
}
if (c4) {
if (az <= -ax && bz <= -bx && cz <= -cx) {
for (w = d; w != null; w = w.next) {
v = w.vertex;
if (-v.cameraX < v.cameraZ) {
faceCulling |= 4;
break;
}
}
if (w == null) continue;
} else if (az > -ax && bz > -bx && cz > -cx) {
for (w = d; w != null; w = w.next) {
v = w.vertex;
if (-v.cameraX >= v.cameraZ) {
faceCulling |= 4;
break;
}
}
} else {
faceCulling |= 4;
}
}
if (c8) {
if (az <= ax && bz <= bx && cz <= cx) {
for (w = d; w != null; w = w.next) {
v = w.vertex;
if (v.cameraX < v.cameraZ) {
faceCulling |= 8;
break;
}
}
if (w == null) continue;
} else if (az > ax && bz > bx && cz > cx) {
for (w = d; w != null; w = w.next) {
v = w.vertex;
if (v.cameraX >= v.cameraZ) {
faceCulling |= 8;
break;
}
}
} else {
faceCulling |= 8;
}
}
if (c16) {
if (az <= -ay && bz <= -by && cz <= -cy) {
for (w = d; w != null; w = w.next) {
v = w.vertex;
if (-v.cameraY < v.cameraZ) {
faceCulling |= 16;
break;
}
}
if (w == null) continue;
} else if (az > -ay && bz > -by && cz > -cy) {
for (w = d; w != null; w = w.next) {
v = w.vertex;
if (-v.cameraY >= v.cameraZ) {
faceCulling |= 16;
break;
}
}
} else {
faceCulling |= 16;
}
}
if (c32) {
if (az <= ay && bz <= by && cz <= cy) {
for (w = d; w != null; w = w.next) {
v = w.vertex;
if (v.cameraY < v.cameraZ) {
faceCulling |= 32;
break;
}
}
if (w == null) continue;
} else if (az > ay && bz > by && cz > cy) {
for (w = d; w != null; w = w.next) {
v = w.vertex;
if (v.cameraY >= v.cameraZ) {
faceCulling |= 32;
break;
}
}
} else {
faceCulling |= 32;
}
}
if (faceCulling > 0) {
wFirst = null;
wLast = null;
w = face.wrapper;
while (w != null) {
wNew = w.create();
wNew.vertex = w.vertex;
if (wFirst != null) wLast.next = wNew; else wFirst = wNew;
wLast = wNew;
w = w.next;
}
// Клиппинг по передней стороне
if (faceCulling & 1) {
a = wLast.vertex;
az = a.cameraZ;
for (w = wFirst,wFirst = null,wLast = null; w != null; w = wNext) {
wNext = w.next;
b = w.vertex;
bz = b.cameraZ;
if (bz > near && az <= near || bz <= near && az > near) {
t = (near - az)/(bz - az);
v = b.create();
camera.lastVertex.next = v;
camera.lastVertex = v;
v.cameraX = a.cameraX + (b.cameraX - a.cameraX)*t;
v.cameraY = a.cameraY + (b.cameraY - a.cameraY)*t;
v.cameraZ = az + (bz - az)*t;
v.u = a.u + (b.u - a.u)*t;
v.v = a.v + (b.v - a.v)*t;
wNew = w.create();
wNew.vertex = v;
if (wFirst != null) wLast.next = wNew; else wFirst = wNew;
wLast = wNew;
}
if (bz > near) {
if (wFirst != null) wLast.next = w; else wFirst = w;
wLast = w;
w.next = null;
} else {
w.vertex = null;
w.next = Wrapper.collector;
Wrapper.collector = w;
}
a = b;
az = bz;
}
if (wFirst == null) continue;
}
// Клиппинг по задней стороне
if (faceCulling & 2) {
a = wLast.vertex;
az = a.cameraZ;
for (w = wFirst,wFirst = null,wLast = null; w != null; w = wNext) {
wNext = w.next;
b = w.vertex;
bz = b.cameraZ;
if (bz < far && az >= far || bz >= far && az < far) {
t = (far - az)/(bz - az);
v = b.create();
camera.lastVertex.next = v;
camera.lastVertex = v;
v.cameraX = a.cameraX + (b.cameraX - a.cameraX)*t;
v.cameraY = a.cameraY + (b.cameraY - a.cameraY)*t;
v.cameraZ = az + (bz - az)*t;
v.u = a.u + (b.u - a.u)*t;
v.v = a.v + (b.v - a.v)*t;
wNew = w.create();
wNew.vertex = v;
if (wFirst != null) wLast.next = wNew; else wFirst = wNew;
wLast = wNew;
}
if (bz < far) {
if (wFirst != null) wLast.next = w; else wFirst = w;
wLast = w;
w.next = null;
} else {
w.vertex = null;
w.next = Wrapper.collector;
Wrapper.collector = w;
}
a = b;
az = bz;
}
if (wFirst == null) continue;
}
// Клиппинг по левой стороне
if (faceCulling & 4) {
a = wLast.vertex;
ax = a.cameraX;
az = a.cameraZ;
for (w = wFirst,wFirst = null,wLast = null; w != null; w = wNext) {
wNext = w.next;
b = w.vertex;
bx = b.cameraX;
bz = b.cameraZ;
if (bz > -bx && az <= -ax || bz <= -bx && az > -ax) {
t = (ax + az)/(ax + az - bx - bz);
v = b.create();
camera.lastVertex.next = v;
camera.lastVertex = v;
v.cameraX = ax + (bx - ax)*t;
v.cameraY = a.cameraY + (b.cameraY - a.cameraY)*t;
v.cameraZ = az + (bz - az)*t;
v.u = a.u + (b.u - a.u)*t;
v.v = a.v + (b.v - a.v)*t;
wNew = w.create();
wNew.vertex = v;
if (wFirst != null) wLast.next = wNew; else wFirst = wNew;
wLast = wNew;
}
if (bz > -bx) {
if (wFirst != null) wLast.next = w; else wFirst = w;
wLast = w;
w.next = null;
} else {
w.vertex = null;
w.next = Wrapper.collector;
Wrapper.collector = w;
}
a = b;
ax = bx;
az = bz;
}
if (wFirst == null) continue;
}
// Клипинг по правой стороне
if (faceCulling & 8) {
a = wLast.vertex;
ax = a.cameraX;
az = a.cameraZ;
for (w = wFirst,wFirst = null,wLast = null; w != null; w = wNext) {
wNext = w.next;
b = w.vertex;
bx = b.cameraX;
bz = b.cameraZ;
if (bz > bx && az <= ax || bz <= bx && az > ax) {
t = (az - ax)/(az - ax + bx - bz);
v = b.create();
camera.lastVertex.next = v;
camera.lastVertex = v;
v.cameraX = ax + (bx - ax)*t;
v.cameraY = a.cameraY + (b.cameraY - a.cameraY)*t;
v.cameraZ = az + (bz - az)*t;
v.u = a.u + (b.u - a.u)*t;
v.v = a.v + (b.v - a.v)*t;
wNew = w.create();
wNew.vertex = v;
if (wFirst != null) wLast.next = wNew; else wFirst = wNew;
wLast = wNew;
}
if (bz > bx) {
if (wFirst != null) wLast.next = w; else wFirst = w;
wLast = w;
w.next = null;
} else {
w.vertex = null;
w.next = Wrapper.collector;
Wrapper.collector = w;
}
a = b;
ax = bx;
az = bz;
}
if (wFirst == null) continue;
}
// Клипинг по верхней стороне
if (faceCulling & 16) {
a = wLast.vertex;
ay = a.cameraY;
az = a.cameraZ;
for (w = wFirst,wFirst = null,wLast = null; w != null; w = wNext) {
wNext = w.next;
b = w.vertex;
by = b.cameraY;
bz = b.cameraZ;
if (bz > -by && az <= -ay || bz <= -by && az > -ay) {
t = (ay + az)/(ay + az - by - bz);
v = b.create();
camera.lastVertex.next = v;
camera.lastVertex = v;
v.cameraX = a.cameraX + (b.cameraX - a.cameraX)*t;
v.cameraY = ay + (by - ay)*t;
v.cameraZ = az + (bz - az)*t;
v.u = a.u + (b.u - a.u)*t;
v.v = a.v + (b.v - a.v)*t;
wNew = w.create();
wNew.vertex = v;
if (wFirst != null) wLast.next = wNew; else wFirst = wNew;
wLast = wNew;
}
if (bz > -by) {
if (wFirst != null) wLast.next = w; else wFirst = w;
wLast = w;
w.next = null;
} else {
w.vertex = null;
w.next = Wrapper.collector;
Wrapper.collector = w;
}
a = b;
ay = by;
az = bz;
}
if (wFirst == null) continue;
}
// Клипинг по нижней стороне
if (faceCulling & 32) {
a = wLast.vertex;
ay = a.cameraY;
az = a.cameraZ;
for (w = wFirst,wFirst = null,wLast = null; w != null; w = wNext) {
wNext = w.next;
b = w.vertex;
by = b.cameraY;
bz = b.cameraZ;
if (bz > by && az <= ay || bz <= by && az > ay) {
t = (az - ay)/(az - ay + by - bz);
v = b.create();
camera.lastVertex.next = v;
camera.lastVertex = v;
v.cameraX = a.cameraX + (b.cameraX - a.cameraX)*t;
v.cameraY = ay + (by - ay)*t;
v.cameraZ = az + (bz - az)*t;
v.u = a.u + (b.u - a.u)*t;
v.v = a.v + (b.v - a.v)*t;
wNew = w.create();
wNew.vertex = v;
if (wFirst != null) wLast.next = wNew; else wFirst = wNew;
wLast = wNew;
}
if (bz > by) {
if (wFirst != null) wLast.next = w; else wFirst = w;
wLast = w;
w.next = null;
} else {
w.vertex = null;
w.next = Wrapper.collector;
Wrapper.collector = w;
}
a = b;
ay = by;
az = bz;
}
if (wFirst == null) continue;
}
}
}
}
var newFace:Face = face.create();
camera.lastFace.next = newFace;
camera.lastFace = newFace;
newFace.material = face.material;
var newVertex:Vertex;
if (faceCulling > 0) {
for (w = wFirst; w != null; w = w.next) {
v = w.vertex;
if (v.value != null) {
w.vertex = v.value;
} else if (v.transformID > 0) {
newVertex = v.create();
camera.lastVertex.next = newVertex;
camera.lastVertex = newVertex;
newVertex.cameraX = v.cameraX;
newVertex.cameraY = v.cameraY;
newVertex.cameraZ = v.cameraZ;
newVertex.u = v.u;
newVertex.v = v.v;
v.value = newVertex;
w.vertex = newVertex;
}
}
} else {
wFirst = null;
for (w = face.wrapper; w != null; w = w.next) {
wNew = w.create();
v = w.vertex;
if (v.value == null) {
newVertex = v.create();
camera.lastVertex.next = newVertex;
camera.lastVertex = newVertex;
newVertex.cameraX = v.cameraX;
newVertex.cameraY = v.cameraY;
newVertex.cameraZ = v.cameraZ;
newVertex.u = v.u;
newVertex.v = v.v;
v.value = newVertex;
}
wNew.vertex = v.value;
if (wFirst != null) {
wLast.next = wNew;
} else {
wFirst = wNew;
}
wLast = wNew;
}
}
newFace.wrapper = wFirst;
if (first != null) {
last.processNext = newFace;
} else {
first = newFace;
}
last = newFace;
}
return first;
}
}
}

View File

@@ -0,0 +1,118 @@
package alternativa.engine3d.objects {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Canvas;
import alternativa.engine3d.core.Clipping;
import alternativa.engine3d.core.Face;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Sorting;
import alternativa.engine3d.core.Vertex;
import alternativa.engine3d.core.Wrapper;
import alternativa.engine3d.materials.TextureMaterial;
import flash.geom.Matrix;
import flash.geom.Point;
use namespace alternativa3d;
public class SkyBox extends Mesh {
static public const LEFT:String = "left";
static public const RIGHT:String = "right";
static public const BACK:String = "back";
static public const FRONT:String = "front";
static public const BOTTOM:String = "bottom";
static public const TOP:String = "top";
private var leftFace:Face;
private var rightFace:Face;
private var backFace:Face;
private var frontFace:Face;
private var bottomFace:Face;
private var topFace:Face;
public function SkyBox(size:Number, left:TextureMaterial, right:TextureMaterial, back:TextureMaterial, front:TextureMaterial, bottom:TextureMaterial, top:TextureMaterial, uvPadding:Number = 0) {
size *= 0.5;
var a:Vertex = addVertex(-size, -size, size, uvPadding, uvPadding);
var b:Vertex = addVertex(-size, -size, -size, uvPadding, 1 - uvPadding);
var c:Vertex = addVertex(-size, size, -size, 1 - uvPadding, 1 - uvPadding);
var d:Vertex = addVertex(-size, size, size, 1 - uvPadding, uvPadding);
leftFace = addQuadFace(a, b, c, d, left);
a = addVertex(size, size, size, uvPadding, uvPadding);
b = addVertex(size, size, -size, uvPadding, 1 - uvPadding);
c = addVertex(size, -size, -size, 1 - uvPadding, 1 - uvPadding);
d = addVertex(size, -size, size, 1 - uvPadding, uvPadding);
rightFace = addQuadFace(a, b, c, d, right);
a = addVertex(size, -size, size, uvPadding, uvPadding);
b = addVertex(size, -size, -size, uvPadding, 1 - uvPadding);
c = addVertex(-size, -size, -size, 1 - uvPadding, 1 - uvPadding);
d = addVertex(-size, -size, size, 1 - uvPadding, uvPadding);
backFace = addQuadFace(a, b, c, d, back);
a = addVertex(-size, size, size, uvPadding, uvPadding);
b = addVertex(-size, size, -size, uvPadding, 1 - uvPadding);
c = addVertex(size, size, -size, 1 - uvPadding, 1 - uvPadding);
d = addVertex(size, size, size, 1 - uvPadding, uvPadding);
frontFace = addQuadFace(a, b, c, d, front);
a = addVertex(-size, size, -size, uvPadding, uvPadding);
b = addVertex(-size, -size, -size, uvPadding, 1 - uvPadding);
c = addVertex(size, -size, -size, 1 - uvPadding, 1 - uvPadding);
d = addVertex(size, size, -size, 1 - uvPadding, uvPadding);
bottomFace = addQuadFace(a, b, c, d, bottom);
a = addVertex(-size, -size, size, uvPadding, uvPadding);
b = addVertex(-size, size, size, uvPadding, 1 - uvPadding);
c = addVertex(size, size, size, 1 - uvPadding, 1 - uvPadding);
d = addVertex(size, -size, size, 1 - uvPadding, uvPadding);
topFace = addQuadFace(a, b, c, d, top);
calculateBounds();
calculateNormals(true);
clipping = Clipping.FACE_CLIPPING;
sorting = Sorting.NONE;
}
public function getSide(side:String):Face {
switch (side) {
case LEFT:
return leftFace;
case RIGHT:
return rightFace;
case BACK:
return backFace;
case FRONT:
return frontFace;
case BOTTOM:
return bottomFace;
case TOP:
return topFace;
}
return null;
}
public function transformUV(side:String, matrix:Matrix):void {
var face:Face = getSide(side);
if (face != null) {
for (var wrapper:Wrapper = face.wrapper; wrapper != null; wrapper = wrapper.next) {
var vertex:Vertex = wrapper.vertex;
var res:Point = matrix.transformPoint(new Point(vertex.u, vertex.v));
vertex.u = res.x;
vertex.v = res.y;
}
}
}
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
object.culling &= ~3;
super.draw(camera, object, parentCanvas);
}
}
}

View File

@@ -0,0 +1,585 @@
package alternativa.engine3d.objects {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Canvas;
import alternativa.engine3d.core.Debug;
import alternativa.engine3d.core.Face;
import alternativa.engine3d.core.Geometry;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Vertex;
import alternativa.engine3d.core.Wrapper;
import alternativa.engine3d.materials.Material;
import flash.display.Sprite;
import flash.geom.Matrix3D;
use namespace alternativa3d;
/**
* Плоский, всегда развёрнутый к камере трёхмерный объект
*/
public class Sprite3D extends Object3D {
/**
* Материал
*/
public var material:Material;
/**
* Режим сортировки на случай конфликта
* 0 - без сортировки
* 1 - сортировка по средним Z
* 2 - построение динамического BSP при отрисовке
* 3 - проход по предрасчитанному BSP
*/
public var sorting:int = 0;
/**
* X точки привязки
*/
public var originX:Number = 0.5;
/**
* Y точки привязки
*/
public var originY:Number = 0.5;
/**
* Режим отсечения объекта по пирамиде видимости камеры.
* 0 - весь объект
* 2 - клиппинг граней по пирамиде видимости камеры
*/
public var clipping:int = 2;
/**
* Угол поворота в радианах в плоскости экрана
*/
public var rotation:Number = 0;
/**
* Ширина
*/
public var width:Number;
/**
* Высота
*/
public var height:Number;
/**
* Зависимость размера на экране от удалённости от камеры
*/
public var perspectiveScale:Boolean = true;
// Текстурная матрица
static private var tma:Number;
static private var tmb:Number;
static private var tmc:Number;
static private var tmd:Number;
static private var tmtx:Number;
static private var tmty:Number;
public function Sprite3D(width:Number = 100, height:Number = 100, material:Material = null) {
this.width = width;
this.height = height;
this.material = material;
}
public function calculateResolution(textureWidth:int, textureHeight:int, type:int = 1, matrix:Matrix3D = null):Number {
var w:Number = width;
var h:Number = height;
if (matrix != null) {
var object:Object3D = new Object3D();
object.setMatrix(matrix);
object.composeMatrix();
var scale:Number = (Math.sqrt(object.ma*object.ma + object.me*object.me + object.mi*object.mi) + Math.sqrt(object.mb*object.mb + object.mf*object.mf + object.mj*object.mj) + Math.sqrt(object.mc*object.mc + object.mg*object.mg + object.mk*object.mk))/3;
w *= scale;
h *= scale;
}
w /= textureWidth;
h /= textureHeight;
if (type == 0) {
return w;
} else if (type == 1) {
return (w + h)/2;
} else if (type == 2) {
return (w < h) ? w : h;
} else {
return (w > h) ? w : h;
}
}
override alternativa3d function draw(camera:Camera3D, object:Object3D, parentCanvas:Canvas):void {
if (material == null) return;
var canvas:Canvas;
var debug:int;
var face:Face = calculateFace(camera, object);
if (face != null) {
// Дебаг
if (camera.debug && (debug = camera.checkInDebug(this)) > 0) {
canvas = parentCanvas.getChildCanvas(object, true, false);
if (debug & Debug.EDGES) Debug.drawEdges(camera, canvas, face, 0xFFFFFF);
if (debug & Debug.BOUNDS) Debug.drawBounds(camera, canvas, object, boundMinX, boundMinY, boundMinZ, boundMaxX, boundMaxY, boundMaxZ);
}
// Отрисовка
canvas = parentCanvas.getChildCanvas(object, true, false, object.alpha, object.blendMode, object.colorTransform, object.filters);
material.drawViewAligned(camera, canvas, face, object.ml, tma, tmb, tmc, tmd, tmtx, tmty);
}
}
override alternativa3d function getGeometry(camera:Camera3D, object:Object3D):Geometry {
if (material == null) return null;
var face:Face = calculateFace(camera, object);
if (face != null) {
var geometry:Geometry = Geometry.create();
geometry.interactiveObject = object;
geometry.faceStruct = face;
geometry.ma = object.ma;
geometry.mb = object.mb;
geometry.mc = object.mc;
geometry.md = object.md;
geometry.me = object.me;
geometry.mf = object.mf;
geometry.mg = object.mg;
geometry.mh = object.mh;
geometry.mi = object.mi;
geometry.mj = object.mj;
geometry.mk = object.mk;
geometry.ml = object.ml;
geometry.alpha = object.alpha;
geometry.blendMode = object.blendMode;
geometry.colorTransform = object.colorTransform;
geometry.filters = object.filters;
geometry.sorting = sorting;
geometry.viewAligned = true;
geometry.tma = tma;
geometry.tmb = tmb;
geometry.tmc = tmc;
geometry.tmd = tmd;
geometry.tmtx = tmtx;
geometry.tmty = tmty;
if (camera.debug) geometry.debug = camera.checkInDebug(this);
return geometry;
} else {
return null;
}
}
private function calculateFace(camera:Camera3D, object:Object3D):Face {
var culling:int = object.culling & 60;
var z:Number = object.ml;
var size:Number;
var ax:Number;
var ay:Number;
var az:Number;
var bx:Number;
var by:Number;
var cx:Number;
var cy:Number;
var dx:Number;
var dy:Number;
var first:Vertex;
var last:Vertex;
// Выход по ближнему или дальнему расстоянию отсечения
if (z <= camera.nearClipping || z >= camera.farClipping) return null;
// Проекция
var projectionX:Number = camera.viewSizeX/z;
var projectionY:Number = camera.viewSizeY/z;
var projectionZ:Number = camera.focalLength/z;
// Учёт искажения матрицы камеры под 90 градусов
var perspectiveScaleX:Number = camera.focalLength/camera.viewSizeX;
var perspectiveScaleY:Number = camera.focalLength/camera.viewSizeY;
// Нахождение среднего размера спрайта
ax = object.ma/perspectiveScaleX;
ay = object.me/perspectiveScaleY;
az = object.mi;
size = Math.sqrt(ax*ax + ay*ay + az*az);
ax = object.mb/perspectiveScaleX;
ay = object.mf/perspectiveScaleY;
az = object.mj;
size += Math.sqrt(ax*ax + ay*ay + az*az);
ax = object.mc/perspectiveScaleX;
ay = object.mg/perspectiveScaleY;
az = object.mk;
size += Math.sqrt(ax*ax + ay*ay + az*az);
size /= 3;
// Учёт флага масштабирования
if (!perspectiveScale) size /= projectionZ;
// Если не задано вращение
if (rotation == 0) {
// Размеры спрайта в матрице камеры
var cameraWidth:Number = size*width*perspectiveScaleX;
var cameraHeight:Number = size*height*perspectiveScaleY;
ax = object.md - originX*cameraWidth;
ay = object.mh - originY*cameraHeight;
cx = ax + cameraWidth;
cy = ay + cameraHeight;
// Подготовка смещения матрицы отрисовки
tmtx = ax*projectionX;
tmty = ay*projectionY;
// Отсечение по пирамиде видимости
if (culling > 0) {
if (ax > z || ay > z || cx < -z || cy < -z) return null;
if (clipping == 2) {
if (ax < -z) ax = -z;
if (ay < -z) ay = -z;
if (cx > z) cx = z;
if (cy > z) cy = z;
}
}
// Создание вершин
first = Vertex.createList(4);
last = first;
last.cameraX = ax;
last.cameraY = ay;
last.cameraZ = z;
last = last.next;
last.cameraX = ax;
last.cameraY = cy;
last.cameraZ = z;
last = last.next;
last.cameraX = cx;
last.cameraY = cy;
last.cameraZ = z;
last = last.next;
last.cameraX = cx;
last.cameraY = ay;
last.cameraZ = z;
// Подготовка матрицы отрисовки
tma = size*projectionZ*width;
tmb = 0;
tmc = 0;
tmd = size*projectionZ*height;
} else {
// Расчёт векторов ширины и высоты
var sin:Number = -Math.sin(rotation)*size;
var cos:Number = Math.cos(rotation)*size;
var cameraWidthX:Number = cos*width*perspectiveScaleX;
var cameraWidthY:Number = -sin*width*perspectiveScaleY;
var cameraHeightX:Number = sin*height*perspectiveScaleX;
var cameraHeightY:Number = cos*height*perspectiveScaleY;
ax = object.md - originX*cameraWidthX - originY*cameraHeightX;
ay = object.mh - originX*cameraWidthY - originY*cameraHeightY;
bx = ax + cameraHeightX;
by = ay + cameraHeightY;
cx = ax + cameraWidthX + cameraHeightX;
cy = ay + cameraWidthY + cameraHeightY;
dx = ax + cameraWidthX;
dy = ay + cameraWidthY;
// Подготовка смещения матрицы отрисовки
tmtx = ax*projectionX;
tmty = ay*projectionY;
// Отсечение по пирамиде видимости
if (culling > 0) {
if (clipping == 1) {
if ((culling & 4) && z <= -ax && z <= -bx && z <= -cx && z <= -dx) return null;
if ((culling & 8) && z <= ax && z <= bx && z <= cx && z <= dx) return null;
if ((culling & 16) && z <= -ay && z <= -by && z <= -cy && z <= -dy) return null;
if ((culling & 32) && z <= ay && z <= by && z <= cy && z <= dy) return null;
// Создание вершин
first = Vertex.createList(4);
last = first;
last.cameraX = ax;
last.cameraY = ay;
last.cameraZ = z;
last = last.next;
last.cameraX = ax + cameraHeightX;
last.cameraY = ay + cameraHeightY;
last.cameraZ = z;
last = last.next;
last.cameraX = ax + cameraWidthX + cameraHeightX;
last.cameraY = ay + cameraWidthY + cameraHeightY;
last.cameraZ = z;
last = last.next;
last.cameraX = ax + cameraWidthX;
last.cameraY = ay + cameraWidthY;
last.cameraZ = z;
} else {
if (culling & 4) {
if (z <= -ax && z <= -bx && z <= -cx && z <= -dx) {
return null;
} else if (z > -ax && z > -bx && z > -cx && z > -dx) {
culling &= 59;
}
}
if (culling & 8) {
if (z <= ax && z <= bx && z <= cx && z <= dx) {
return null;
} else if (z > ax && z > bx && z > cx && z > dx) {
culling &= 55;
}
}
if (culling & 16) {
if (z <= -ay && z <= -by && z <= -cy && z <= -dy) {
return null;
} else if (z > -ay && z > -by && z > -cy && z > -dy) {
culling &= 47;
}
}
if (culling & 32) {
if (z <= ay && z <= by && z <= cy && z <= dy) {
return null;
} else if (z > ay && z > by && z > cy && z > dy) {
culling &= 31;
}
}
// Создание вершин
first = Vertex.createList(4);
last = first;
last.cameraX = ax;
last.cameraY = ay;
last.cameraZ = z;
last = last.next;
last.cameraX = ax + cameraHeightX;
last.cameraY = ay + cameraHeightY;
last.cameraZ = z;
last = last.next;
last.cameraX = ax + cameraWidthX + cameraHeightX;
last.cameraY = ay + cameraWidthY + cameraHeightY;
last.cameraZ = z;
last = last.next;
last.cameraX = ax + cameraWidthX;
last.cameraY = ay + cameraWidthY;
last.cameraZ = z;
if (culling > 0) {
var t:Number;
var a:Vertex;
var b:Vertex;
var v:Vertex;
var next:Vertex;
// Клиппинг по левой стороне
if (culling & 4) {
a = last;
ax = a.cameraX;
for (b = first,first = null,last = null; b != null; b = next) {
next = b.next;
bx = b.cameraX;
if (z > -bx && z <= -ax || z <= -bx && z > -ax) {
t = (ax + z)/(ax - bx);
v = b.create();
v.cameraX = ax + (bx - ax)*t;
v.cameraY = a.cameraY + (b.cameraY - a.cameraY)*t;
v.cameraZ = z;
if (first != null) last.next = v; else first = v;
last = v;
}
if (z > -bx) {
if (first != null) last.next = b; else first = b;
last = b;
b.next = null;
} else {
b.next = Vertex.collector;
Vertex.collector = b;
}
a = b;
ax = bx;
}
if (first == null) return null;
}
// Клиппинг по правой стороне
if (culling & 8) {
a = last;
ax = a.cameraX;
for (b = first,first = null,last = null; b != null; b = next) {
next = b.next;
bx = b.cameraX;
if (z > bx && z <= ax || z <= bx && z > ax) {
t = (z - ax)/(bx - ax);
v = b.create();
v.cameraX = ax + (bx - ax)*t;
v.cameraY = a.cameraY + (b.cameraY - a.cameraY)*t;
v.cameraZ = z;
if (first != null) last.next = v; else first = v;
last = v;
}
if (z > bx) {
if (first != null) last.next = b; else first = b;
last = b;
b.next = null;
} else {
b.next = Vertex.collector;
Vertex.collector = b;
}
a = b;
ax = bx;
}
if (first == null) return null;
}
// Клиппинг по верхней стороне
if (culling & 16) {
a = last;
ay = a.cameraY;
for (b = first,first = null,last = null; b != null; b = next) {
next = b.next;
by = b.cameraY;
if (z > -by && z <= -ay || z <= -by && z > -ay) {
t = (ay + z)/(ay - by);
v = b.create();
v.cameraX = a.cameraX + (b.cameraX - a.cameraX)*t;
v.cameraY = ay + (by - ay)*t;
v.cameraZ = z;
if (first != null) last.next = v; else first = v;
last = v;
}
if (z > -by) {
if (first != null) last.next = b; else first = b;
last = b;
b.next = null;
} else {
b.next = Vertex.collector;
Vertex.collector = b;
}
a = b;
ay = by;
}
if (first == null) return null;
}
// Клиппинг по нижней стороне
if (culling & 32) {
a = last;
ay = a.cameraY;
for (b = first,first = null,last = null; b != null; b = next) {
next = b.next;
by = b.cameraY;
if (z > by && z <= ay || z <= by && z > ay) {
t = (z - ay)/(by - ay);
v = b.create();
v.cameraX = a.cameraX + (b.cameraX - a.cameraX)*t;
v.cameraY = ay + (by - ay)*t;
v.cameraZ = z;
if (first != null) last.next = v; else first = v;
last = v;
}
if (z > by) {
if (first != null) last.next = b; else first = b;
last = b;
b.next = null;
} else {
b.next = Vertex.collector;
Vertex.collector = b;
}
a = b;
ay = by;
}
if (first == null) return null;
}
}
}
} else {
// Создание вершин
first = Vertex.createList(4);
last = first;
last.cameraX = ax;
last.cameraY = ay;
last.cameraZ = z;
last = last.next;
last.cameraX = ax + cameraHeightX;
last.cameraY = ay + cameraHeightY;
last.cameraZ = z;
last = last.next;
last.cameraX = ax + cameraWidthX + cameraHeightX;
last.cameraY = ay + cameraWidthY + cameraHeightY;
last.cameraZ = z;
last = last.next;
last.cameraX = ax + cameraWidthX;
last.cameraY = ay + cameraWidthY;
last.cameraZ = z;
}
// Подготовка матрицы отрисовки
tma = cos*projectionZ*width;
tmb = -sin*projectionZ*width;
tmc = sin*projectionZ*height;
tmd = cos*projectionZ*height;
}
// Отправка на отложенное удаление
camera.lastVertex.next = first;
camera.lastVertex = last;
// Создание грани
var face:Face = Face.create();
face.material = material;
camera.lastFace.next = face;
camera.lastFace = face;
var wrapper:Wrapper = Wrapper.create();
face.wrapper = wrapper;
wrapper.vertex = first;
for (first = first.next; first != null; first = first.next) {
wrapper.next = wrapper.create();
wrapper = wrapper.next;
wrapper.vertex = first;
}
return face;
}
override alternativa3d function updateBounds(bounds:Object3D, transformation:Object3D = null):void {
// Расчёт локального радиуса
var w:Number = ((originX >= 0.5) ? originX : (1 - originX))*width;
var h:Number = ((originY >= 0.5) ? originY : (1 - originY))*height;
var radius:Number = Math.sqrt(w*w + h*h);
var cx:Number = 0;
var cy:Number = 0;
var cz:Number = 0;
if (transformation != null) {
// Нахождение среднего размера спрайта
var ax:Number = transformation.ma;
var ay:Number = transformation.me;
var az:Number = transformation.mi;
var size:Number = Math.sqrt(ax*ax + ay*ay + az*az);
ax = transformation.mb;
ay = transformation.mf;
az = transformation.mj;
size += Math.sqrt(ax*ax + ay*ay + az*az);
ax = transformation.mc;
ay = transformation.mg;
az = transformation.mk;
size += Math.sqrt(ax*ax + ay*ay + az*az);
radius *= size/3;
cx = transformation.md;
cy = transformation.mh;
cz = transformation.ml;
}
if (cx - radius < bounds.boundMinX) bounds.boundMinX = cx - radius;
if (cx + radius > bounds.boundMaxX) bounds.boundMaxX = cx + radius;
if (cy - radius < bounds.boundMinY) bounds.boundMinY = cy - radius;
if (cy + radius > bounds.boundMaxY) bounds.boundMaxY = cy + radius;
if (cz - radius < bounds.boundMinZ) bounds.boundMinZ = cz - radius;
if (cz + radius > bounds.boundMaxZ) bounds.boundMaxZ = cz + radius;
}
public function copyFrom(source:Sprite3D):void {
name = source.name;
visible = source.visible;
alpha = source.alpha;
blendMode = source.blendMode;
colorTransform = source.colorTransform;
filters = source.filters;
x = source.x;
y = source.y;
z = source.z;
rotationX = source.rotationX;
rotationY = source.rotationY;
rotationZ = source.rotationZ;
scaleX = source.scaleX;
scaleY = source.scaleY;
scaleZ = source.scaleZ;
boundMinX = source.boundMinX;
boundMinY = source.boundMinY;
boundMinZ = source.boundMinZ;
boundMaxX = source.boundMaxX;
boundMaxY = source.boundMaxY;
boundMaxZ = source.boundMaxZ;
clipping = source.clipping;
sorting = source.sorting;
material = source.material;
originX = source.originX;
originY = source.originY;
rotation = source.rotation;
perspectiveScale = source.perspectiveScale;
}
override public function clone():Object3D {
var sprite:Sprite3D = new Sprite3D(width, height, material);
sprite.cloneBaseProperties(this);
sprite.clipping = clipping;
sprite.sorting = sorting;
sprite.originX = originX;
sprite.originY = originY;
sprite.rotation = rotation;
sprite.perspectiveScale = perspectiveScale;
return sprite;
}
}
}

View File

@@ -0,0 +1,12 @@
package alternativa.engine3d.objects {
import alternativa.engine3d.core.Vertex;
public class VertexBinding {
public var next:VertexBinding;
public var vertex:Vertex;
public var weight:Number = 0;
}
}

View File

@@ -0,0 +1,194 @@
package alternativa.engine3d.primitives {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.core.Face;
import alternativa.engine3d.core.Vertex;
import alternativa.engine3d.objects.Mesh;
use namespace alternativa3d;
public class Box extends Mesh {
public function Box(width:Number = 100, length:Number = 100, height:Number = 100, widthSegments:uint = 1, lengthSegments:uint = 1, heightSegments:uint = 1, reverse:Boolean = false) {
var wp:int = widthSegments + 1;
var lp:int = lengthSegments + 1;
var hp:int = heightSegments + 1;
var wh:Number = width*0.5;
var lh:Number = length*0.5;
var hh:Number = height*0.5;
var wd:Number = 1/widthSegments;
var ld:Number = 1/lengthSegments;
var hd:Number = 1/heightSegments;
var ws:Number = width/widthSegments;
var ls:Number = length/lengthSegments;
var hs:Number = height/heightSegments;
var x:int;
var y:int;
var z:int;
var v:int = 0;
var vertices:Vector.<Vertex> = new Vector.<Vertex>();
var face:Face;
// Нижняя грань
for (x = 0; x < wp; x++) {
for (y = 0; y < lp; y++) {
vertices[v++] = addVertex(x*ws - wh, y*ls - lh, -hh, (widthSegments - x)*wd, (lengthSegments - y)*ld);
}
}
for (x = 0; x < wp; x++) {
for (y = 0; y < lp; y++) {
if (x < widthSegments && y < lengthSegments) {
if (reverse) {
face = addQuadFace(vertices[(x + 1)*lp + y + 1], vertices[x*lp + y + 1], vertices[x*lp + y], vertices[(x + 1)*lp + y]);
face.normalZ = 1;
face.offset = -hh;
} else {
face = addQuadFace(vertices[(x + 1)*lp + y + 1], vertices[(x + 1)*lp + y], vertices[x*lp + y], vertices[x*lp + y + 1]);
face.normalZ = -1;
face.offset = hh;
}
face.normalX = 0;
face.normalY = 0;
}
}
}
var o:uint = wp*lp;
// Верхняя грань
for (x = 0; x < wp; x++) {
for (y = 0; y < lp; y++) {
vertices[v++] = addVertex(x*ws - wh, y*ls - lh, hh, x*wd, (lengthSegments - y)*ld);
}
}
for (x = 0; x < wp; x++) {
for (y = 0; y < lp; y++) {
if (x < widthSegments && y < lengthSegments) {
if (reverse) {
face = addQuadFace(vertices[o + x*lp + y + 1], vertices[o + (x + 1)*lp + y + 1], vertices[o + (x + 1)*lp + y], vertices[o + x*lp + y]);
face.normalZ = -1;
face.offset = -hh;
} else {
face = addQuadFace(vertices[o + x*lp + y], vertices[o + (x + 1)*lp + y], vertices[o + (x + 1)*lp + y + 1], vertices[o + x*lp + y + 1]);
face.normalZ = 1;
face.offset = hh;
}
face.normalX = 0;
face.normalY = 0;
}
}
}
o += wp*lp;
// Передняя грань
for (x = 0; x < wp; x++) {
for (z = 0; z < hp; z++) {
vertices[v++] = addVertex(x*ws - wh, -lh, z*hs - hh, x*wd, (heightSegments - z)*hd);
}
}
for (x = 0; x < wp; x++) {
for (z = 0; z < hp; z++) {
if (x < widthSegments && z < heightSegments) {
if (reverse) {
face = addQuadFace(vertices[o + x*hp + z + 1], vertices[o + (x + 1)*hp + z + 1], vertices[o + (x + 1)*hp + z], vertices[o + x*hp + z]);
face.normalY = 1;
face.offset = -lh;
} else {
face = addQuadFace(vertices[o + x*hp + z], vertices[o + (x + 1)*hp + z], vertices[o + (x + 1)*hp + z + 1], vertices[o + x*hp + z + 1]);
face.normalY = -1;
face.offset = lh;
}
face.normalX = 0;
face.normalZ = 0;
}
}
}
o += wp*hp;
// Задняя грань
for (x = 0; x < wp; x++) {
for (z = 0; z < hp; z++) {
vertices[v++] = addVertex(x*ws - wh, lh, z*hs - hh, (widthSegments - x)*wd, (heightSegments - z)*hd);
}
}
for (x = 0; x < wp; x++) {
for (z = 0; z < hp; z++) {
if (x < widthSegments && z < heightSegments) {
if (reverse) {
face = addQuadFace(vertices[o + (x + 1)*hp + z], vertices[o + (x + 1)*hp + z + 1], vertices[o + x*hp + z + 1], vertices[o + x*hp + z]);
face.normalY = -1;
face.offset = -lh;
} else {
face = addQuadFace(vertices[o + x*hp + z], vertices[o + x*hp + z + 1], vertices[o + (x + 1)*hp + z + 1], vertices[o + (x + 1)*hp + z]);
face.normalY = 1;
face.offset = lh;
}
face.normalX = 0;
face.normalZ = 0;
}
}
}
o += wp*hp;
// Левая грань
for (y = 0; y < lp; y++) {
for (z = 0; z < hp; z++) {
vertices[v++] = addVertex(-wh, y*ls - lh, z*hs - hh, (lengthSegments - y)*ld, (heightSegments - z)*hd);
}
}
for (y = 0; y < lp; y++) {
for (z = 0; z < hp; z++) {
if (y < lengthSegments && z < heightSegments) {
if (reverse) {
face = addQuadFace(vertices[o + (y + 1)*hp + z], vertices[o + (y + 1)*hp + z + 1], vertices[o + y*hp + z + 1], vertices[o + y*hp + z]);
face.normalX = 1;
face.offset = -wh;
} else {
face = addQuadFace(vertices[o + y*hp + z], vertices[o + y*hp + z + 1], vertices[o + (y + 1)*hp + z + 1], vertices[o + (y + 1)*hp + z]);
face.normalX = -1;
face.offset = wh;
}
face.normalY = 0;
face.normalZ = 0;
}
}
}
o += lp*hp;
// Правая грань
for (y = 0; y < lp; y++) {
for (z = 0; z < hp; z++) {
vertices[v++] = addVertex(wh, y*ls - lh, z*hs - hh, y*ld, (heightSegments - z)*hd);
}
}
for (y = 0; y < lp; y++) {
for (z = 0; z < hp; z++) {
if (y < lengthSegments && z < heightSegments) {
if (reverse) {
face = addQuadFace(vertices[o + y*hp + z + 1], vertices[o + (y + 1)*hp + z + 1], vertices[o + (y + 1)*hp + z], vertices[o + y*hp + z]);
face.normalX = -1;
face.offset = -wh;
} else {
face = addQuadFace(vertices[o + y*hp + z], vertices[o + (y + 1)*hp + z], vertices[o + (y + 1)*hp + z + 1], vertices[o + y*hp + z + 1]);
face.normalX = 1;
face.offset = wh;
}
face.normalY = 0;
face.normalZ = 0;
}
}
}
// Установка границ
boundMinX = -wh;
boundMinY = -lh;
boundMinZ = -hh;
boundMaxX = wh;
boundMaxY = lh;
boundMaxZ = hh;
}
}
}

View File

@@ -0,0 +1,251 @@
package alternativa.engine3d.primitives {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.objects.Mesh;
use namespace alternativa3d;
/**
* @private
*/
public class GeoSphere extends Mesh {
public function GeoSphere(radius:Number = 100, segments:uint = 2, reverse:Boolean = false) {
/*
const sections:uint = 20;
var i:uint;
var theta:Number;
var sin:Number;
var cos:Number;
// z расстояние до нижней и верхней крышки полюса
var subz:Number = 4.472136E-001*radius;
// радиус на расстоянии subz
var subrad:Number = 2*subz;
var v:uint = 0;
var f:uint = sections*segments*segments;
addVertex(0, 0, radius, 0, 0);
// vertices[v++] = 0;
// vertices[v++] = 0;
// vertices[v++] = radius;
// Создание вершин верхней крышки
for (i = 0; i < 5; i++) {
theta = Math.PI*2*i/5;
sin = Math.sin(theta);
cos = Math.cos(theta);
addVertex(subrad*cos, subrad*sin, subz, 0, 0);
// vertices[v++] = subrad*cos;
// vertices[v++] = subrad*sin;
// vertices[v++] = subz;
}
// Создание вершин нижней крышки
for (i = 0; i < 5; i++) {
theta = Math.PI*((i << 1) + 1)/5;
sin = Math.sin(theta);
cos = Math.cos(theta);
addVertex(subrad*cos, subrad*sin, -subz, 0, 0);
// vertices[v++] = subrad*cos;
// vertices[v++] = subrad*sin;
// vertices[v++] = -subz;
}
addVertex(0, 0, -radius, 0, 0);
// vertices[v++] = 0;
// vertices[v++] = 0;
// vertices[v++] = -radius;
if (segments > 1) {
for (i = 1; i < 6; i++) {
v = interpolate(0, i, segments, v);
}
for (i = 1; i < 6; i++) {
v = interpolate(i, i % 5 + 1, segments, v);
}
for (i = 1; i < 6; i++) {
v = interpolate(i, i + 5, segments, v);
}
for (i = 1; i < 6; i++) {
v = interpolate(i, (i + 3) % 5 + 6, segments, v);
}
for (i = 1; i < 6; i++) {
v = interpolate(i + 5, i % 5 + 6, segments, v);
}
for (i = 6; i < 11; i++) {
v = interpolate(11, i, segments, v);
}
for (f = 0; f < 5; f++) {
for (i = 1; i <= segments - 2; i++) {
v = interpolate(12 + f*(segments - 1) + i, 12 + (f + 1) % 5*(segments - 1) + i, i + 1, v);
}
}
for (f = 0; f < 5; f++) {
for (i = 1; i <= segments - 2; i++) {
v = interpolate(12 + (f + 15)*(segments - 1) + i, 12 + (f + 10)*(segments - 1) + i, i + 1, v);
}
}
for (f = 0; f < 5; f++) {
for (i = 1; i <= segments - 2; i++) {
v = interpolate(12 + ((f + 1) % 5 + 15)*(segments - 1) + segments - 2 - i, 12 + (f + 10)*(segments - 1) + segments - 2 - i, i + 1, v);
}
}
for (f = 0; f < 5; f++) {
for (i = 1; i <= segments - 2; i++) {
v = interpolate(12 + ((f + 1) % 5 + 25)*(segments - 1) + i, 12 + (f + 25)*(segments - 1) + i, i + 1, v);
}
}
}
// for (i = 0; i < numVertices; i++) {
// var j:uint = i*3;
// uvts[j] = Math.atan2(vertices[j + 1], vertices[j])/(Math.PI*2);
// uvts[j] = 0.5 + (reverse ? -uvts[j] : uvts[j]);
// uvts[j + 1] = 0.5 + Math.asin(vertices[j + 2]/radius)/Math.PI;
// }
var num:uint = 0;
for (f = 0; f <= sections - 1; f++) {
for (var row:uint = 0; row <= segments - 1; row++) {
for (var column:uint = 0; column <= row; column++) {
var a:uint = findVertices(segments, f, row, column);
var b:uint = findVertices(segments, f, row + 1, column);
var c:uint = findVertices(segments, f, row + 1, column + 1);
if (reverse) {
indices[num++] = a;
indices[num++] = c;
indices[num++] = b;
} else {
indices[num++] = a;
indices[num++] = b;
indices[num++] = c;
}
if (column < row) {
var d:uint = findVertices(segments, f, row, column + 1);
if (reverse) {
indices[num++] = a;
indices[num++] = d;
indices[num++] = c;
} else {
indices[num++] = a;
indices[num++] = c;
indices[num++] = d;
}
}
}
}
}
boundMinX = boundMinY = boundMinZ = -radius;
boundMaxX = boundMaxY = boundMaxZ = radius;
}
private function interpolate(a:uint, b:uint, num:uint, v:uint):uint {
a *= 3;
b *= 3;
var ax:Number = vertices[a];
var ay:Number = vertices[a + 1];
var az:Number = vertices[a + 2];
var bx:Number = vertices[b];
var by:Number = vertices[b + 1];
var bz:Number = vertices[b + 2];
var cos:Number = (ax*bx + ay*by + az*bz)/(ax*ax + ay*ay + az*az);
cos = (cos < -1) ? -1 : ((cos > 1) ? 1 : cos);
var theta:Number = Math.acos(cos);
var sin:Number = Math.sin(theta);
for (var e:uint = 1; e < num; e++) {
var theta1:Number = theta*e/num;
var theta2:Number = theta*(num - e)/num;
var st1:Number = Math.sin(theta1);
var st2:Number = Math.sin(theta2);
vertices[v++] = (ax*st2 + bx*st1)/sin;
vertices[v++] = (ay*st2 + by*st1)/sin;
vertices[v++] = (az*st2 + bz*st1)/sin;
}
return v;
//*/
}
/*
private function findVertices(segments:uint, section:uint, row:uint, column:uint):uint {
if (row == 0) {
if (section < 5) {
return (0);
}
if (section > 14) {
return (11);
}
return (section - 4);
}
if (row == segments && column == 0) {
if (section < 5) {
return (section + 1);
}
if (section < 10) {
return ((section + 4) % 5 + 6);
}
if (section < 15) {
return ((section + 1) % 5 + 1);
}
return ((section + 1) % 5 + 6);
}
if (row == segments && column == segments) {
if (section < 5) {
return ((section + 1) % 5 + 1);
}
if (section < 10) {
return (section + 1);
}
if (section < 15) {
return (section - 9);
}
return (section - 9);
}
if (row == segments) {
if (section < 5) {
return (12 + (5 + section)*(segments - 1) + column - 1);
}
if (section < 10) {
return (12 + (20 + (section + 4) % 5)*(segments - 1) + column - 1);
}
if (section < 15) {
return (12 + (section - 5)*(segments - 1) + segments - 1 - column);
}
return (12 + (5 + section)*(segments - 1) + segments - 1 - column);
}
if (column == 0) {
if (section < 5) {
return (12 + section*(segments - 1) + row - 1);
}
if (section < 10) {
return (12 + (section % 5 + 15)*(segments - 1) + row - 1);
}
if (section < 15) {
return (12 + ((section + 1) % 5 + 15)*(segments - 1) + segments - 1 - row);
}
return (12 + ((section + 1) % 5 + 25)*(segments - 1) + row - 1);
}
if (column == row) {
if (section < 5) {
return (12 + (section + 1) % 5*(segments - 1) + row - 1);
}
if (section < 10) {
return (12 + (section % 5 + 10)*(segments - 1) + row - 1);
}
if (section < 15) {
return (12 + (section % 5 + 10)*(segments - 1) + segments - row - 1);
}
return (12 + (section % 5 + 25)*(segments - 1) + row - 1);
}
return (12 + 30*(segments - 1) + section*(segments - 1)*(segments - 2)/2 + (row - 1)*(row - 2)/2 + column - 1);
}
//*/
}
}

View File

@@ -0,0 +1,66 @@
package alternativa.engine3d.primitives {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.objects.Mesh;
use namespace alternativa3d;
/**
* @private
*/
public class Plane extends Mesh {
/**
*
* @param width
* @param length
* @param widthSegments
* @param lengthSegments
*/
public function Plane(width:Number = 100, length:Number = 100, widthSegments:uint = 1, lengthSegments:uint = 1) {
/*
var wp:uint = widthSegments + 1;
var lp:uint = lengthSegments + 1;
createEmptyGeometry(wp*lp, (widthSegments*lengthSegments) << 2);
var wh:Number = width*0.5;
var lh:Number = length*0.5;
var wd:Number = 1/widthSegments;
var ld:Number = 1/lengthSegments;
var ws:Number = width/widthSegments;
var ls:Number = length/lengthSegments;
var x:uint;
var y:uint;
var z:uint;
var v:uint = 0;
var f:uint = 0;
// Верхняя грань
for (x = 0; x < wp; x++) {
for (y = 0; y < lp; y++) {
vertices[v] = x*ws - wh;
uvts[v++] = x*wd;
vertices[v] = y*ls - lh;
uvts[v++] = (lengthSegments - y)*ld;
vertices[v++] = 0;
if (x < widthSegments && y < lengthSegments) {
indices[f++] = x*lp + y;
indices[f++] = (x + 1)*lp + y;
indices[f++] = (x + 1)*lp + y + 1;
indices[f++] = x*lp + y;
indices[f++] = (x + 1)*lp + y + 1;
indices[f++] = x*lp + y + 1;
}
}
}
// Установка границ
_boundBox = new BoundBox();
_boundBox.setSize(-wh, -lh, 0, wh, lh, 0);
*/
}
}
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<componentPackage>
<component id="Alternativa3D" class="alternativa.Alternativa3D"/>
</componentPackage>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<actionScriptProperties mainApplicationPath="Engine3DLibrary.as" version="3">
<compiler additionalCompilerArguments="-compute-digest=false" copyDependentFiles="false" enableModuleDebug="false" flexSDK="Flex 4.0" generateAccessible="false" htmlExpressInstall="true" htmlGenerate="false" htmlHistoryManagement="false" htmlPlayerVersion="10.0.0" htmlPlayerVersionCheck="true" outputFolderPath="bin" sourceFolderPath="src" strict="true" useApolloConfig="false" verifyDigests="true" warn="true">
<compilerSourcePath/>
<libraryPath defaultLinkType="1">
<libraryPathEntry kind="4" path="">
<excludedEntries>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/flex.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_agent.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="1" linkType="1" path="${PROJECT_FRAMEWORKS}/locale/{locale}"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/textLayout_conversion.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/player/{targetPlayerMajorVersion}" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_dmv.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/framework.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/text_importExport.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/text_edit.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/textLayout_edit.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_flashflexkit.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="2" path="${PROJECT_FRAMEWORKS}/libs/utilities.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/framework_textLayout.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/qtp.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/textLayout_core.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/rpc.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/textLayout_textField.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/datavisualization.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/flex4.swc" useDefaultLinkType="false"/>
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/text_model.swc" useDefaultLinkType="false"/>
</excludedEntries>
</libraryPathEntry>
</libraryPath>
<sourceAttachmentPath/>
</compiler>
<applications>
<application path="Engine3DLibrary.as"/>
</applications>
<modules/>
<buildCSSFiles/>
</actionScriptProperties>

View File

@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<flexLibProperties version="1">
<includeClasses>
<classEntry path="alternativa.engine3d.core.Face"/>
<classEntry path="alternativa.engine3d.objects.Axes"/>
<classEntry path="alternativa.engine3d.core.KDNode"/>
<classEntry path="alternativa.engine3d.materials.TextureMaterial"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeCamera"/>
<classEntry path="alternativa.engine3d.core.Object3D"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeController"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeNode"/>
<classEntry path="alternativa.engine3d.objects.VertexBinding"/>
<classEntry path="alternativa.engine3d.primitives.GeoSphere"/>
<classEntry path="alternativa.engine3d.animation.AnimationTimer"/>
<classEntry path="alternativa.engine3d.loaders.MaterialLoader"/>
<classEntry path="alternativa.engine3d.animation.MatrixAnimation"/>
<classEntry path="alternativa.engine3d.objects.Mesh"/>
<classEntry path="alternativa.engine3d.animation.TransformAnimation"/>
<classEntry path="alternativa.engine3d.animation.keys.ValueKey"/>
<classEntry path="alternativa.engine3d.primitives.Plane"/>
<classEntry path="alternativa.Alternativa3D"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeAlternativa3DObject"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeLogger"/>
<classEntry path="alternativa.engine3d.animation.keys.MatrixKey"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeInstanceMaterial"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeParam"/>
<classEntry path="alternativa.engine3d.core.MipMapping"/>
<classEntry path="alternativa.engine3d.materials.Material"/>
<classEntry path="alternativa.engine3d.core.Clipping"/>
<classEntry path="alternativa.engine3d.core.Object3DContainer"/>
<classEntry path="alternativa.engine3d.core.Geometry"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaePrimitive"/>
<classEntry path="alternativa.engine3d.controllers.SimpleObjectController"/>
<classEntry path="alternativa.engine3d.core.View"/>
<classEntry path="alternativa.engine3d.loaders.Parser3DS"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeMaterial"/>
<classEntry path="alternativa.engine3d.animation.keys.PointKey"/>
<classEntry path="alternativa.engine3d.loaders.events.LoaderProgressEvent"/>
<classEntry path="alternativa.engine3d.objects.Occluder"/>
<classEntry path="alternativa.engine3d.objects.Bone"/>
<classEntry path="alternativa.engine3d.animation.keys.Key"/>
<classEntry path="alternativa.engine3d.objects.Joint"/>
<classEntry path="alternativa.engine3d.objects.Skin"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeInput"/>
<classEntry path="alternativa.engine3d.containers.ConflictContainer"/>
<classEntry path="alternativa.engine3d.core.Sorting"/>
<classEntry path="alternativa.engine3d.objects.Sprite3D"/>
<classEntry path="alternativa.engine3d.objects.LOD"/>
<classEntry path="alternativa.engine3d.objects.SkyBox"/>
<classEntry path="alternativa.engine3d.loaders.ParserCollada"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeChannel"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeInstanceController"/>
<classEntry path="alternativa.engine3d.core.Debug"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeVertices"/>
<classEntry path="alternativa.engine3d.loaders.events.LoaderErrorEvent"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeAnimatedObject"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeArray"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeDocument"/>
<classEntry path="alternativa.engine3d.core.Wrapper"/>
<classEntry path="alternativa.engine3d.objects.AnimSprite"/>
<classEntry path="alternativa.engine3d.loaders.collada.collada"/>
<classEntry path="alternativa.engine3d.containers.ZSortContainer"/>
<classEntry path="alternativa.engine3d.core.Canvas"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeEffect"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeElement"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeImage"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeSampler"/>
<classEntry path="alternativa.engine3d.containers.KDTree"/>
<classEntry path="alternativa.engine3d.objects.Reference"/>
<classEntry path="alternativa.engine3d.materials.FillMaterial"/>
<classEntry path="alternativa.engine3d.animation.AnimationController"/>
<classEntry path="alternativa.engine3d.core.Vertex"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeGeometry"/>
<classEntry path="alternativa.engine3d.core.MouseEvent3D"/>
<classEntry path="alternativa.engine3d.core.Camera3D"/>
<classEntry path="alternativa.engine3d.loaders.events.LoaderEvent"/>
<classEntry path="alternativa.engine3d.animation.Track"/>
<classEntry path="alternativa.engine3d.animation.AnimationState"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeEffectParam"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeVisualScene"/>
<classEntry path="alternativa.engine3d.animation.Animation"/>
<classEntry path="alternativa.engine3d.loaders.collada.DaeSource"/>
<classEntry path="alternativa.engine3d.alternativa3d"/>
<classEntry path="alternativa.engine3d.primitives.Box"/>
</includeClasses>
<includeResources/>
<namespaceManifests/>
</flexLibProperties>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>Alternativa3D</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.adobe.flexbuilder.project.flexbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.maven.ide.eclipse.maven2Nature</nature>
<nature>com.adobe.flexbuilder.project.flexlibnature</nature>
<nature>com.adobe.flexbuilder.project.actionscriptnature</nature>
</natures>
<linkedResources>
</linkedResources>
</projectDescription>

View File

@@ -0,0 +1,83 @@
#Fri Feb 12 14:04:16 YEKT 2010
BlankLines.BeforeFirstMember=0
BlankLines.BeforePackage=0
BlankLines.BeforeTypeInPackage=1
BlankLines.BeforeTypeOutsidePackage=1
BlankLines.BetweenFields=0
BlankLines.BetweenFunctions=1
BlankLines.BetweenMembergrous=1
BlankLines.EatBlankLines=false
Braces.Catch=false
Braces.DoWhile=false
Braces.Else=false
Braces.Finally=false
Braces.For=false
Braces.Function=false
Braces.Getter=false
Braces.If=false
Braces.Method=false
Braces.ObjectInitialiser=false
Braces.Package=false
Braces.Setter=false
Braces.Switch=false
Braces.Try=false
Braces.Type=false
Braces.While=false
Braces.With=false
Indentation.Case=true
Indentation.Catch=true
Indentation.DoWhile=true
Indentation.Else=true
Indentation.Finally=true
Indentation.For=true
Indentation.Functions=true
Indentation.Getters=true
Indentation.If=true
Indentation.Methods=true
Indentation.Package=true
Indentation.Setters=true
Indentation.SpacesPerTab=4
Indentation.Switch=true
Indentation.Try=true
Indentation.Types=true
Indentation.UseSpaces=false
Indentation.UseTabs=true
Indentation.While=true
WhiteSpace.BlankAfterAdditiveOperator=true
WhiteSpace.BlankAfterArgumentComma=true
WhiteSpace.BlankAfterArgumentList=false
WhiteSpace.BlankAfterArrayInitialiser=false
WhiteSpace.BlankAfterAssign=true
WhiteSpace.BlankAfterBinaryOperator=true
WhiteSpace.BlankAfterCommaInArrayInit=true
WhiteSpace.BlankAfterCommaInObjectInit=true
WhiteSpace.BlankAfterConditionalColon=true
WhiteSpace.BlankAfterConditionalHook=true
WhiteSpace.BlankAfterConditionalOperator=true
WhiteSpace.BlankAfterEquality=true
WhiteSpace.BlankAfterFunctionName=false
WhiteSpace.BlankAfterMultiplicativeOperator=true
WhiteSpace.BlankAfterPrefixOperator=false
WhiteSpace.BlankAfterRelationalOperator=true
WhiteSpace.BlankAfterShiftOperator=true
WhiteSpace.BlankAfterVaraiableTypeColon=true
WhiteSpace.BlankAfterVariableInitialiser=true
WhiteSpace.BlankBeforeAdditiveOperator=true
WhiteSpace.BlankBeforeArgumentComma=false
WhiteSpace.BlankBeforeArgumentList=false
WhiteSpace.BlankBeforeArrayInitialiser=false
WhiteSpace.BlankBeforeAssign=true
WhiteSpace.BlankBeforeBinaryOperator=true
WhiteSpace.BlankBeforeCommaInArrayInit=false
WhiteSpace.BlankBeforeCommaInObjectInit=false
WhiteSpace.BlankBeforeConditionalColon=true
WhiteSpace.BlankBeforeConditionalHook=true
WhiteSpace.BlankBeforeConditionalOperator=true
WhiteSpace.BlankBeforeEquality=true
WhiteSpace.BlankBeforeMultiplicativeOperator=true
WhiteSpace.BlankBeforePostfixOperator=false
WhiteSpace.BlankBeforeRelationalOperator=true
WhiteSpace.BlankBeforeShiftOperator=true
WhiteSpace.BlankBeforeVaraiableTypeColon=true
WhiteSpace.BlankBeforeVariableInitialiser=true
eclipse.preferences.version=1

View File

@@ -0,0 +1,7 @@
#Tue Feb 16 17:42:22 YEKT 2010
com.powerflasher.fdt.core.DefaultOutputFolder=bin
com.powerflasher.fdt.core.Language=AS3
com.powerflasher.fdt.core.LanguageType=Flex_3_SDK_0_for_FP_10
com.powerflasher.fdt.core.useProjectFormatterSettings=false
com.powerflasher.fdt.core.useProjectProblems=false
eclipse.preferences.version=1

View File

@@ -0,0 +1,3 @@
#Thu Feb 14 09:12:30 YEKT 2008
eclipse.preferences.version=1
encoding/<project>=UTF-8

View File

@@ -0,0 +1,3 @@
#Tue Nov 13 17:53:36 YEKT 2007
eclipse.preferences.version=1
org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false

View File

@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="flex" name="Flex">
<configuration flex_sdk="flex_sdk_4 (player 10)">
<option name="DO_BUILD" value="true" />
<option name="OUTPUT_TYPE" value="Library" />
<option name="USE_DEFAULT_SDK_CONFIG_FILE" value="true" />
<option name="USE_CUSTOM_CONFIG_FILE" value="false" />
<option name="CUSTOM_CONFIG_FILE" value="" />
<option name="USE_CUSTOM_CONFIG_FILE_FOR_TESTS" value="false" />
<option name="CUSTOM_CONFIG_FILE_FOR_TESTS" value="" />
<option name="APPLICATION_ENTRY_POINT" value="" />
<option name="MAIN_CLASS" value="" />
<option name="OUTPUT_FILE_NAME" value="Alternativa3D-2.7.3.0-SNAPSHOT.swc" />
<option name="USE_FACET_COMPILE_OUTPUT_PATH" value="true" />
<option name="FACET_COMPILE_OUTPUT_PATH" value="$MODULE_DIR$/target" />
<option name="INCLUDE_RESOURCE_FILES_IN_SWC" value="false" />
<option name="TARGET_PLAYER_VERSION" value="10.0.0" />
<option name="STATIC_LINK_RUNTIME_SHARED_LIBRARIES" value="false" />
<option name="USE_LOCALE_SETTINGS" value="false" />
<option name="LOCALE" value="en_US" />
<option name="ADDITIONAL_COMPILER_OPTIONS" value="" />
<option name="VERSION" value="3" />
<option name="PATH_TO_SERVICES_CONFIG_XML" value="" />
<option name="CONTEXT_ROOT" value="" />
<NAMESPACE_AND_MANIFEST_FILE_INFO_LIST />
<CONDITIONAL_COMPILATION_DEFINITION_LIST />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/target/generated-sources/platform" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target/classes" />
<excludeFolder url="file://$MODULE_DIR$/target/surefire-reports" />
<excludeFolder url="file://$MODULE_DIR$/target/test-classes" />
</content>
<orderEntry type="jdk" jdkName="flex_sdk_4 (player 10)" jdkType="Flex SDK Type" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library">
<library name="AUTOGENERATED library equal to Flex SDK flex_sdk_4 (player 10)">
<CLASSES>
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/player/10/playerglobal.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/textLayout_core.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/textLayout_edit.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/textLayout_conversion.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/framework_textLayout.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/flex4.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/flex.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/textLayout_textField.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/rpc.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/utilities.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/libs/framework.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/locale/en_US/rpc_rb.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/locale/en_US/flex4_rb.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/locale/en_US/framework_rb.swc!/" />
<root url="jar://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/locale/en_US/airframework_rb.swc!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/air/ServiceMonitor/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/air/ApplicationUpdater/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/utilities/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/airframework/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/wireframe/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/flex4/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/framework_textLayout/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/haloclassic/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/framework/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/rpc/src" />
<root url="file://$MODULE_DIR$/../../../opt/flex_sdk_4/frameworks/projects/flex/src" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="library" name="Maven: platform.clients.fp10.libraries:AlternativaOSGi:swc:2.0.3.0" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:playerglobal:swc:10-4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:textLayout_conversion:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:textLayout_core:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:flex4:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:framework:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:applicationupdater:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:applicationupdater_ui:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:textLayout_edit:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:flex:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:utilities:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:rpc:swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:rpc:rb.swc:4.0.0.4021" level="project" />
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:framework:rb.swc:4.0.0.4021" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.maven.wagon:wagon-webdav:1.0-beta-2" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: slide:slide-webdavlib:2.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: commons-httpclient:commons-httpclient:2.0.2" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: commons-logging:commons-logging:1.0.4" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: jdom:jdom:1.0" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: de.zeigermann.xml:xml-im-exporter:1.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.maven.wagon:wagon-provider-api:1.0-beta-2" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.codehaus.plexus:plexus-utils:1.0.4" level="project" />
</component>
</module>

View File

@@ -0,0 +1 @@
Bundle-Name: platform.clients.fp10.libraries.Alternativa3D

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