mirror of
https://github.com/MapMakersAndProgrammers/alternativa3d-archive.git
synced 2025-10-27 18:29:07 -07:00
a3d7 (2.7) moved to another folder
This commit is contained in:
39
Alternativa3D7v2/2.7.3.0/.actionScriptProperties
Normal file
39
Alternativa3D7v2/2.7.3.0/.actionScriptProperties
Normal 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>
|
||||
87
Alternativa3D7v2/2.7.3.0/.flexLibProperties
Normal file
87
Alternativa3D7v2/2.7.3.0/.flexLibProperties
Normal 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>
|
||||
21
Alternativa3D7v2/2.7.3.0/.project
Normal file
21
Alternativa3D7v2/2.7.3.0/.project
Normal 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>
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
#Thu Feb 14 09:12:30 YEKT 2008
|
||||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
||||
@@ -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
|
||||
103
Alternativa3D7v2/2.7.3.0/Alternativa3D.iml
Normal file
103
Alternativa3D7v2/2.7.3.0/Alternativa3D.iml
Normal 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>
|
||||
|
||||
1
Alternativa3D7v2/2.7.3.0/META-INF/MANIFEST.MF
Normal file
1
Alternativa3D7v2/2.7.3.0/META-INF/MANIFEST.MF
Normal file
@@ -0,0 +1 @@
|
||||
Bundle-Name: platform.clients.fp10.libraries.Alternativa3D
|
||||
33
Alternativa3D7v2/2.7.3.0/pom.xml
Normal file
33
Alternativa3D7v2/2.7.3.0/pom.xml
Normal 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>
|
||||
14
Alternativa3D7v2/2.7.3.0/src/alternativa/Alternativa3D.as
Normal file
14
Alternativa3D7v2/2.7.3.0/src/alternativa/Alternativa3D.as
Normal file
@@ -0,0 +1,14 @@
|
||||
package alternativa {
|
||||
|
||||
/**
|
||||
* Класс содержит информацию о версии библиотеки.
|
||||
* Также используется для интеграции библиотеки в среду разработки Adobe Flash.
|
||||
*/
|
||||
public class Alternativa3D {
|
||||
|
||||
/**
|
||||
* Версия библиотеки в формате: поколение.feature-версия.fix-версия
|
||||
*/
|
||||
public static const version:String = "7.0.0";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package alternativa.engine3d {
|
||||
public namespace alternativa3d = "http://alternativaplatform.com/en/alternativa3d";
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package alternativa.engine3d.animation {
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
|
||||
public class ObjectAnimation extends Animation {
|
||||
|
||||
public var object:Object3D;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
108
Alternativa3D7v2/2.7.3.0/src/alternativa/engine3d/core/Canvas.as
Normal file
108
Alternativa3D7v2/2.7.3.0/src/alternativa/engine3d/core/Canvas.as
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
190
Alternativa3D7v2/2.7.3.0/src/alternativa/engine3d/core/Debug.as
Normal file
190
Alternativa3D7v2/2.7.3.0/src/alternativa/engine3d/core/Debug.as
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
179
Alternativa3D7v2/2.7.3.0/src/alternativa/engine3d/core/Face.as
Normal file
179
Alternativa3D7v2/2.7.3.0/src/alternativa/engine3d/core/Face.as
Normal 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;
|
||||
}*/
|
||||
|
||||
}
|
||||
}
|
||||
1235
Alternativa3D7v2/2.7.3.0/src/alternativa/engine3d/core/Geometry.as
Normal file
1235
Alternativa3D7v2/2.7.3.0/src/alternativa/engine3d/core/Geometry.as
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package alternativa.engine3d.core {
|
||||
|
||||
public class Sorting {
|
||||
|
||||
/**
|
||||
* Грани не сортируются.
|
||||
*/
|
||||
static public const NONE:int = 0;
|
||||
/**
|
||||
* Грани сортируются по средним Z.
|
||||
*/
|
||||
static public const AVERAGE_Z:int = 1;
|
||||
/**
|
||||
* Грани при отрисовке образуют BSP-дерево.
|
||||
*/
|
||||
static public const DYNAMIC_BSP:int = 2;
|
||||
/**
|
||||
* Грани находятся в BSP-дереве.
|
||||
*/
|
||||
static public const STATIC_BSP:int = 3;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
328
Alternativa3D7v2/2.7.3.0/src/alternativa/engine3d/core/View.as
Normal file
328
Alternativa3D7v2/2.7.3.0/src/alternativa/engine3d/core/View.as
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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)];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package alternativa.engine3d.loaders.collada {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public namespace collada = "http://www.collada.org/2005/11/COLLADASchema";
|
||||
}
|
||||
@@ -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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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) + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
3145
Alternativa3D7v2/2.7.3.0/src/alternativa/engine3d/objects/Mesh.as
Normal file
3145
Alternativa3D7v2/2.7.3.0/src/alternativa/engine3d/objects/Mesh.as
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
//*/
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
4
Alternativa3D7v2/2.7.3.0/src/manifest.xml
Normal file
4
Alternativa3D7v2/2.7.3.0/src/manifest.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<componentPackage>
|
||||
<component id="Alternativa3D" class="alternativa.Alternativa3D"/>
|
||||
</componentPackage>
|
||||
39
Alternativa3D7v2/2.7.4.1/.actionScriptProperties
Normal file
39
Alternativa3D7v2/2.7.4.1/.actionScriptProperties
Normal 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>
|
||||
88
Alternativa3D7v2/2.7.4.1/.flexLibProperties
Normal file
88
Alternativa3D7v2/2.7.4.1/.flexLibProperties
Normal 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>
|
||||
21
Alternativa3D7v2/2.7.4.1/.project
Normal file
21
Alternativa3D7v2/2.7.4.1/.project
Normal 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>
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
#Thu Feb 14 09:12:30 YEKT 2008
|
||||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
||||
@@ -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
|
||||
103
Alternativa3D7v2/2.7.4.1/Alternativa3D.iml
Normal file
103
Alternativa3D7v2/2.7.4.1/Alternativa3D.iml
Normal 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>
|
||||
|
||||
1
Alternativa3D7v2/2.7.4.1/META-INF/MANIFEST.MF
Normal file
1
Alternativa3D7v2/2.7.4.1/META-INF/MANIFEST.MF
Normal 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
Reference in New Issue
Block a user