mirror of
https://github.com/MapMakersAndProgrammers/alternativa3d-archive.git
synced 2025-10-26 09:49:07 -07:00
more versions added
This commit is contained in:
BIN
Alternativa3D5/.DS_Store
vendored
Normal file
BIN
Alternativa3D5/.DS_Store
vendored
Normal file
Binary file not shown.
30
Alternativa3D5/5.0.0/.actionScriptProperties
Normal file
30
Alternativa3D5/5.0.0/.actionScriptProperties
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<actionScriptProperties mainApplicationPath="Engine3DLibrary.as" version="3">
|
||||
<compiler additionalCompilerArguments="" copyDependentFiles="false" enableModuleDebug="false" generateAccessible="false" htmlExpressInstall="true" htmlGenerate="false" htmlHistoryManagement="false" htmlPlayerVersion="9.0.0" htmlPlayerVersionCheck="true" outputFolderPath="" 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/qtp.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/framework.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/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="2" path="${PROJECT_FRAMEWORKS}/libs/utilities.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_agent.swc" useDefaultLinkType="false"/>
|
||||
</excludedEntries>
|
||||
</libraryPathEntry>
|
||||
<libraryPathEntry kind="3" linkType="2" path="/AlternativaTypes/AlternativaTypes.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="2" path="/AlternativaUtils/AlternativaUtils.swc" useDefaultLinkType="false"/>
|
||||
</libraryPath>
|
||||
<sourceAttachmentPath/>
|
||||
</compiler>
|
||||
<applications>
|
||||
<application path="Engine3DLibrary.as"/>
|
||||
</applications>
|
||||
<modules/>
|
||||
<buildCSSFiles/>
|
||||
</actionScriptProperties>
|
||||
54
Alternativa3D5/5.0.0/.flexLibProperties
Normal file
54
Alternativa3D5/5.0.0/.flexLibProperties
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<flexLibProperties version="1">
|
||||
<includeClasses>
|
||||
<classEntry path="alternativa.engine3d.Alternativa3DVersion"/>
|
||||
<classEntry path="alternativa.engine3d.alternativa3d"/>
|
||||
<classEntry path="alternativa.engine3d.controllers.CameraController"/>
|
||||
<classEntry path="alternativa.engine3d.core.BSPNode"/>
|
||||
<classEntry path="alternativa.engine3d.core.Camera3D"/>
|
||||
<classEntry path="alternativa.engine3d.core.Face"/>
|
||||
<classEntry path="alternativa.engine3d.core.Mesh"/>
|
||||
<classEntry path="alternativa.engine3d.core.Object3D"/>
|
||||
<classEntry path="alternativa.engine3d.core.Operation"/>
|
||||
<classEntry path="alternativa.engine3d.core.PolyPrimitive"/>
|
||||
<classEntry path="alternativa.engine3d.core.Scene3D"/>
|
||||
<classEntry path="alternativa.engine3d.core.Surface"/>
|
||||
<classEntry path="alternativa.engine3d.core.Vertex"/>
|
||||
<classEntry path="alternativa.engine3d.display.Skin"/>
|
||||
<classEntry path="alternativa.engine3d.display.View"/>
|
||||
<classEntry path="alternativa.engine3d.errors.Engine3DError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.FaceExistsError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.FaceNeedMoreVerticesError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.FaceNotFoundError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.InvalidIDError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.Object3DHierarchyError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.Object3DNotFoundError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.ObjectExistsError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.ObjectNotFoundError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.SurfaceExistsError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.SurfaceNotFoundError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.VertexExistsError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.VertexNotFoundError"/>
|
||||
<classEntry path="alternativa.engine3d.loaders.Loader3DS"/>
|
||||
<classEntry path="alternativa.engine3d.materials.DrawPoint"/>
|
||||
<classEntry path="alternativa.engine3d.materials.FillMaterial"/>
|
||||
<classEntry path="alternativa.engine3d.materials.Material"/>
|
||||
<classEntry path="alternativa.engine3d.materials.SurfaceMaterial"/>
|
||||
<classEntry path="alternativa.engine3d.materials.TextureMaterial"/>
|
||||
<classEntry path="alternativa.engine3d.materials.TextureMaterialPrecision"/>
|
||||
<classEntry path="alternativa.engine3d.materials.WireMaterial"/>
|
||||
<classEntry path="alternativa.engine3d.physics.Collision"/>
|
||||
<classEntry path="alternativa.engine3d.physics.CollisionPlane"/>
|
||||
<classEntry path="alternativa.engine3d.physics.SphereCollider"/>
|
||||
<classEntry path="alternativa.engine3d.primitives.Box"/>
|
||||
<classEntry path="alternativa.engine3d.primitives.Cone"/>
|
||||
<classEntry path="alternativa.engine3d.primitives.GeoPlane"/>
|
||||
<classEntry path="alternativa.engine3d.primitives.GeoSphere"/>
|
||||
<classEntry path="alternativa.engine3d.primitives.Plane"/>
|
||||
<classEntry path="alternativa.engine3d.primitives.Sphere"/>
|
||||
<classEntry path="alternativa.utils.Alternativa3DLabel"/>
|
||||
<classEntry path="alternativa.utils.MeshUtils"/>
|
||||
</includeClasses>
|
||||
<includeResources/>
|
||||
<namespaceManifests/>
|
||||
</flexLibProperties>
|
||||
18
Alternativa3D5/5.0.0/.project
Normal file
18
Alternativa3D5/5.0.0/.project
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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>com.adobe.flexbuilder.project.flexlibnature</nature>
|
||||
<nature>com.adobe.flexbuilder.project.actionscriptnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
17
Alternativa3D5/5.0.0/.settings/.svn/all-wcprops
Normal file
17
Alternativa3D5/5.0.0/.settings/.svn/all-wcprops
Normal file
@@ -0,0 +1,17 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 79
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/.settings
|
||||
END
|
||||
org.eclipse.core.resources.prefs
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 112
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/.settings/org.eclipse.core.resources.prefs
|
||||
END
|
||||
org.eclipse.ltk.core.refactoring.prefs
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 118
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/.settings/org.eclipse.ltk.core.refactoring.prefs
|
||||
END
|
||||
52
Alternativa3D5/5.0.0/.settings/.svn/entries
Normal file
52
Alternativa3D5/5.0.0/.settings/.svn/entries
Normal file
@@ -0,0 +1,52 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/.settings
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
org.eclipse.core.resources.prefs
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
63644c8a8f9fe441148cb750eb3dc2f3
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
org.eclipse.ltk.core.refactoring.prefs
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
85719ffc1d818e46b40e2f90aad31e8c
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
1
Alternativa3D5/5.0.0/.settings/.svn/format
Normal file
1
Alternativa3D5/5.0.0/.settings/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
@@ -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
|
||||
@@ -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
|
||||
35
Alternativa3D5/5.0.0/.svn/all-wcprops
Normal file
35
Alternativa3D5/5.0.0/.svn/all-wcprops
Normal file
@@ -0,0 +1,35 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 69
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0
|
||||
END
|
||||
.flexLibProperties
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 88
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/.flexLibProperties
|
||||
END
|
||||
license.txt
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 81
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/license.txt
|
||||
END
|
||||
.project
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 78
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/.project
|
||||
END
|
||||
license_ru.txt
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 84
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/license_ru.txt
|
||||
END
|
||||
.actionScriptProperties
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 93
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/.actionScriptProperties
|
||||
END
|
||||
95
Alternativa3D5/5.0.0/.svn/entries
Normal file
95
Alternativa3D5/5.0.0/.svn/entries
Normal file
@@ -0,0 +1,95 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2008-08-25T13:15:49.990298Z
|
||||
162
|
||||
int
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
.flexLibProperties
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
065e76c8aded9fbf95d5283b631ea951
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
license.txt
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
e8c7913cf1d91e0581a89566f4f82b00
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
.project
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
5cfe073356c81d48a97889a21f8ec909
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
license_ru.txt
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
b9249bf013e942c694836296539bfccf
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
has-props
|
||||
|
||||
alternativa
|
||||
dir
|
||||
|
||||
.actionScriptProperties
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
fdc75eaaae4ca4683af8b3a74f8e7e21
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
.settings
|
||||
dir
|
||||
|
||||
1
Alternativa3D5/5.0.0/.svn/format
Normal file
1
Alternativa3D5/5.0.0/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
@@ -0,0 +1,5 @@
|
||||
K 13
|
||||
svn:mime-type
|
||||
V 24
|
||||
application/octet-stream
|
||||
END
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<actionScriptProperties mainApplicationPath="Engine3DLibrary.as" version="3">
|
||||
<compiler additionalCompilerArguments="" copyDependentFiles="false" enableModuleDebug="false" generateAccessible="false" htmlExpressInstall="true" htmlGenerate="false" htmlHistoryManagement="false" htmlPlayerVersion="9.0.0" htmlPlayerVersionCheck="true" outputFolderPath="" 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/qtp.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/framework.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/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="2" path="${PROJECT_FRAMEWORKS}/libs/utilities.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_agent.swc" useDefaultLinkType="false"/>
|
||||
</excludedEntries>
|
||||
</libraryPathEntry>
|
||||
<libraryPathEntry kind="3" linkType="2" path="/AlternativaTypes/AlternativaTypes.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="2" path="/AlternativaUtils/AlternativaUtils.swc" useDefaultLinkType="false"/>
|
||||
</libraryPath>
|
||||
<sourceAttachmentPath/>
|
||||
</compiler>
|
||||
<applications>
|
||||
<application path="Engine3DLibrary.as"/>
|
||||
</applications>
|
||||
<modules/>
|
||||
<buildCSSFiles/>
|
||||
</actionScriptProperties>
|
||||
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<flexLibProperties version="1">
|
||||
<includeClasses>
|
||||
<classEntry path="alternativa.engine3d.Alternativa3DVersion"/>
|
||||
<classEntry path="alternativa.engine3d.alternativa3d"/>
|
||||
<classEntry path="alternativa.engine3d.controllers.CameraController"/>
|
||||
<classEntry path="alternativa.engine3d.core.BSPNode"/>
|
||||
<classEntry path="alternativa.engine3d.core.Camera3D"/>
|
||||
<classEntry path="alternativa.engine3d.core.Face"/>
|
||||
<classEntry path="alternativa.engine3d.core.Mesh"/>
|
||||
<classEntry path="alternativa.engine3d.core.Object3D"/>
|
||||
<classEntry path="alternativa.engine3d.core.Operation"/>
|
||||
<classEntry path="alternativa.engine3d.core.PolyPrimitive"/>
|
||||
<classEntry path="alternativa.engine3d.core.Scene3D"/>
|
||||
<classEntry path="alternativa.engine3d.core.Surface"/>
|
||||
<classEntry path="alternativa.engine3d.core.Vertex"/>
|
||||
<classEntry path="alternativa.engine3d.display.Skin"/>
|
||||
<classEntry path="alternativa.engine3d.display.View"/>
|
||||
<classEntry path="alternativa.engine3d.errors.Engine3DError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.FaceExistsError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.FaceNeedMoreVerticesError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.FaceNotFoundError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.InvalidIDError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.Object3DHierarchyError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.Object3DNotFoundError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.ObjectExistsError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.ObjectNotFoundError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.SurfaceExistsError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.SurfaceNotFoundError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.VertexExistsError"/>
|
||||
<classEntry path="alternativa.engine3d.errors.VertexNotFoundError"/>
|
||||
<classEntry path="alternativa.engine3d.loaders.Loader3DS"/>
|
||||
<classEntry path="alternativa.engine3d.materials.DrawPoint"/>
|
||||
<classEntry path="alternativa.engine3d.materials.FillMaterial"/>
|
||||
<classEntry path="alternativa.engine3d.materials.Material"/>
|
||||
<classEntry path="alternativa.engine3d.materials.SurfaceMaterial"/>
|
||||
<classEntry path="alternativa.engine3d.materials.TextureMaterial"/>
|
||||
<classEntry path="alternativa.engine3d.materials.TextureMaterialPrecision"/>
|
||||
<classEntry path="alternativa.engine3d.materials.WireMaterial"/>
|
||||
<classEntry path="alternativa.engine3d.physics.Collision"/>
|
||||
<classEntry path="alternativa.engine3d.physics.CollisionPlane"/>
|
||||
<classEntry path="alternativa.engine3d.physics.SphereCollider"/>
|
||||
<classEntry path="alternativa.engine3d.primitives.Box"/>
|
||||
<classEntry path="alternativa.engine3d.primitives.Cone"/>
|
||||
<classEntry path="alternativa.engine3d.primitives.GeoPlane"/>
|
||||
<classEntry path="alternativa.engine3d.primitives.GeoSphere"/>
|
||||
<classEntry path="alternativa.engine3d.primitives.Plane"/>
|
||||
<classEntry path="alternativa.engine3d.primitives.Sphere"/>
|
||||
<classEntry path="alternativa.utils.Alternativa3DLabel"/>
|
||||
<classEntry path="alternativa.utils.MeshUtils"/>
|
||||
</includeClasses>
|
||||
<includeResources/>
|
||||
<namespaceManifests/>
|
||||
</flexLibProperties>
|
||||
18
Alternativa3D5/5.0.0/.svn/text-base/.project.svn-base
Normal file
18
Alternativa3D5/5.0.0/.svn/text-base/.project.svn-base
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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>com.adobe.flexbuilder.project.flexlibnature</nature>
|
||||
<nature>com.adobe.flexbuilder.project.actionscriptnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
98
Alternativa3D5/5.0.0/.svn/text-base/license.txt.svn-base
Normal file
98
Alternativa3D5/5.0.0/.svn/text-base/license.txt.svn-base
Normal file
@@ -0,0 +1,98 @@
|
||||
Programme Usage "Alternativa3D"
|
||||
License argeement
|
||||
|
||||
Before usage of the programme please review the terms of the present license
|
||||
agreement. Any usage of the programme means total and unconditional consent
|
||||
to conditions of the present license agreement.
|
||||
|
||||
If you do not totally accept terms of the license agreement you do not have
|
||||
the right to use the programme for any purpose.
|
||||
|
||||
1. License
|
||||
|
||||
The present License agreement ("License") is concluded between you, the user
|
||||
("The User"), and Alternativa Game, Ltd., 321-34, Krupskoi st., Perm, Russia,
|
||||
who is the possessor of sole rights to the Programme ("the Rightholder").
|
||||
The license sets conditions of usage of the computer programme "Alternativa3D"
|
||||
by the User.
|
||||
|
||||
1.1. By copying the Programme and installing it to the personal computer
|
||||
the User expresses total and unconditional consent to conditions of the License.
|
||||
|
||||
1.2. Usage of the Programme is permitted only on conditions of the present
|
||||
License. If the User does not totally accept terms of the License, the User does
|
||||
not possess the right to use the Programme for any purpose. Usage of
|
||||
the Programme with violation (nonfulfilment) of any conditions of the License
|
||||
is prohibited.
|
||||
|
||||
1.3. Usage of the Programme on conditions of the present License for personal
|
||||
nonprofit advantage is free of charge. Usage of the Programme on terms and in
|
||||
the ways not provided by the present License is permitted only by conclusion
|
||||
of a special agreement with the Rightholder at the price set by the Rightholder.
|
||||
|
||||
2. Rights to the Programme
|
||||
|
||||
Sole property copyrights to use the Programme reserved to the Rightholder. Those
|
||||
rights of the Rightholder also include the rights to the trade marks and to
|
||||
the information about technologies.
|
||||
|
||||
3. Rights of the User
|
||||
|
||||
3.1. The Rightholder empowers the User with non-exclusive non-transferable right
|
||||
to use the Programme in the following ways:
|
||||
|
||||
3.1.1. To use the Programme for the functional purpose specified, to copy
|
||||
and set it up at personal computer(s) of the User only for the purpose
|
||||
specified. The User is empowered to set up the Programme at unlimited number
|
||||
of personal computers.
|
||||
|
||||
3.2. The Programme shall be used with indication of the trade name
|
||||
"Alternativa3D". The User shall not change the trade name of the Programme,
|
||||
change and/or delete the sign of the copyright of the Rightholder.
|
||||
|
||||
4. Restrictions
|
||||
|
||||
4.1. The User does not possess the right to do or allow to do the following
|
||||
actions regarding the Programme: translation, modification, other retreatment
|
||||
and other usage of the Programme not directly provided by the present License
|
||||
except for usage of the Programme in the volume and in the ways directly
|
||||
provided by the present License, legislation of Russia or other countries
|
||||
effective at the moment of concession of the present License.
|
||||
|
||||
4.2. The user does not possess the right to reproduce and spread the Programme
|
||||
for commercial purposes (at a charge) even as a part of collections of software
|
||||
products without written consent of the Rightholder. Any usage of the Programme
|
||||
by the User which can cause direct or indirect profit-making by the User from
|
||||
the Programme is also prohibited.
|
||||
|
||||
4.3. The User is not allowed to reverse compile, reverse assemble, decipher
|
||||
and do any other actions with the compiled code of the Programme aimed at breach
|
||||
of the protection system of the Programme from unauthorized usage and receipt
|
||||
of information about implementation of algorithms used in the Programme,
|
||||
to create derivative products with usage of the Programme, without written
|
||||
consent of the Rightholder.
|
||||
|
||||
4.4. When placing the finished product using the library Alternativa3D
|
||||
in Internet for the third parties access the User shall place a visual label
|
||||
"Powered by Alternativa3D" on this page with a hyperlink to the website
|
||||
alternativaplatform.com.
|
||||
|
||||
4.5. The present License shall not extend to the subsequent versions
|
||||
of the Program and operates only concerning the files of the Program delivered
|
||||
with it.
|
||||
|
||||
5. Liabilitiy by the License
|
||||
|
||||
5.1. The software is provided as is. The Rightholder shall not provide any
|
||||
guarantees regarding error-free and uninterrupted work of the Programme,
|
||||
compliance of the Programme with specific purposes of the User and also shall
|
||||
not provide any guarantee not directly stated by the present License.
|
||||
|
||||
5.2. In the maximal breadth allowed by the effective legislation the Rightholder
|
||||
as well as his partners shall not be eligible for any direct or indirect
|
||||
consequences of any usage of the Programme and/or any damage to the User
|
||||
and/or the third parties caused by any usage or disuse of the Programme,
|
||||
including possible errors or bugs in the work of the Programme.
|
||||
|
||||
5.3. In case of violation of conditions of the present license agreement
|
||||
by the User, it can be terminated by the Rightholder without additional notice.
|
||||
93
Alternativa3D5/5.0.0/.svn/text-base/license_ru.txt.svn-base
Normal file
93
Alternativa3D5/5.0.0/.svn/text-base/license_ru.txt.svn-base
Normal file
@@ -0,0 +1,93 @@
|
||||
Лицензионное соглашение
|
||||
на использование Программы «Alternativa3D»
|
||||
|
||||
Перед использованием программы, пожалуйста, ознакомьтесь с условиями настоящего
|
||||
лицензионного соглашения. Любое использование вами программы означает полное
|
||||
и безоговорочное принятие вами условий настоящего лицензионного соглашения.
|
||||
|
||||
Если вы не принимаете условия лицензионного соглашения в полном объеме, вы не
|
||||
имеете права использовать программу в каких-либо целях.
|
||||
|
||||
1. Лицензия
|
||||
|
||||
Настоящее Лицензионное соглашение («Лицензия») заключено между Вами,
|
||||
пользователем («Пользователь»), и ООО «Альтернатива», Россия, г. Пермь,
|
||||
ул. Крупской, 34, являющимся правообладателем исключительных прав на Программу
|
||||
(«Правообладатель»). Лицензия устанавливает условия использования Пользователем
|
||||
программы для ЭВМ «Alternativa3D» («Программа»).
|
||||
|
||||
1.1. Копируя Программу и устанавливая ее на свой персональный компьютер,
|
||||
Пользователь выражает свое полное и безоговорочное согласие со всеми условиями
|
||||
Лицензии.
|
||||
|
||||
1.2. Использование Программы разрешается только на условиях настоящей Лицензии.
|
||||
Если Пользователь не принимает условия Лицензии в полном объеме, Пользователь
|
||||
не имеет права использовать Программу в каких-либо целях. Использование
|
||||
Программы с нарушением (невыполнением) какого-либо из условий Лицензии
|
||||
запрещено.
|
||||
|
||||
1.3. Использование Программы на условиях настоящей Лицензии в личных
|
||||
некоммерческих целях осуществляется безвозмездно. Использование Программы на
|
||||
условиях и способами, не предусмотренными настоящей Лицензией, возможно только
|
||||
на основании отдельного соглашения с Правообладателем по цене, устанавливаемой
|
||||
Правообладателем.
|
||||
|
||||
2. Права на Программу
|
||||
Исключительные имущественные авторские права на использование Программы
|
||||
принадлежат Правообладателю.
|
||||
|
||||
3. Права Пользователя
|
||||
|
||||
3.1. Правообладатель предоставляет Пользователю неисключительное непередаваемое
|
||||
право использовать Программу следующими способами:
|
||||
|
||||
3.1.1. Применять Программу по прямому функциональному назначению, в целях чего
|
||||
произвести ее копирование и установку на персональном(ых) компьютере(ах)
|
||||
Пользователя. Пользователь вправе произвести установку Программы на
|
||||
неограниченное число персональных компьютеров.
|
||||
|
||||
3.2. Программа должна использоваться под фирменным наименованием:
|
||||
«Alternativa3D». Пользователь не вправе изменять фирменное наименование
|
||||
Программы, изменять и/или удалять обозначение авторских прав Правообладателя
|
||||
(copyright).
|
||||
|
||||
4. Ограничения
|
||||
|
||||
4.1. За исключением использования в объемах и способами, прямо предусмотренными
|
||||
настоящей Лицензией или законодательством РФ или законодательством других стран,
|
||||
действующим на момент предоставления Лицензии, Пользователь не имеет право
|
||||
осуществлять и/или разрешать осуществлять в отношении Программы следующие
|
||||
действия: перевод; модификацию; иную переработку, а также иное использование
|
||||
Программы, прямо не предусмотренное настоящей Лицензией.
|
||||
|
||||
4.2. Пользователь не имеет право воспроизводить и распространять Программу
|
||||
в коммерческих целях (за плату), в том числе в составе сборников программных
|
||||
продуктов, без письменного согласия Правообладателя.
|
||||
|
||||
4.3. Пользователю не разрешается изменять, декомпилировать, дизассемблировать,
|
||||
дешифровывать и производить иные действия с объектным кодом Программы, имеющие
|
||||
целью нарушение системы защиты Программы от несанкционированного использования
|
||||
и получение информации о реализации алгоритмов, используемых в Программе,
|
||||
создавать производные программные продукты с использованием Программы, без
|
||||
письменного согласия Правообладателя.
|
||||
|
||||
4.4. При размещении готового продукта, использующего библиотеку Alternativa3D,
|
||||
в интернет для доступа третьих лиц, пользователь обязан разместить на этой же
|
||||
странице видимую надпись «Powered by Alternativa3D» со ссылкой на сайт
|
||||
alternativaplatform.com.
|
||||
|
||||
5. Ответственность по Лицензии
|
||||
|
||||
5.1. Программное Обеспечение предоставляется на условиях «как есть» (as is).
|
||||
Правообладатель не предоставляет никаких гарантий в отношении безошибочной
|
||||
и бесперебойной работы Программы, соответствия Программы конкретным целям
|
||||
Пользователя, а также не предоставляет никаких иных гарантий, прямо не указанных
|
||||
в настоящей Лицензии.
|
||||
|
||||
5.2. В максимальной степени, допустимой действующим законодательством,
|
||||
Правообладатель, равно как и его партнеры, не несет никакой ответственности
|
||||
за какие-либо прямые или косвенные последствия какого-либо использования или
|
||||
невозможности использования Программы и/или ущерб, причиненный Пользователю
|
||||
и/или третьим сторонам в результате какого-либо использования или
|
||||
неиспользования Программы, в том числе из-за возможных ошибок или сбоев в работе
|
||||
Программы.
|
||||
5
Alternativa3D5/5.0.0/alternativa/.svn/all-wcprops
Normal file
5
Alternativa3D5/5.0.0/alternativa/.svn/all-wcprops
Normal file
@@ -0,0 +1,5 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 81
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa
|
||||
END
|
||||
34
Alternativa3D5/5.0.0/alternativa/.svn/entries
Normal file
34
Alternativa3D5/5.0.0/alternativa/.svn/entries
Normal file
@@ -0,0 +1,34 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
utils
|
||||
dir
|
||||
|
||||
engine3d
|
||||
dir
|
||||
|
||||
1
Alternativa3D5/5.0.0/alternativa/.svn/format
Normal file
1
Alternativa3D5/5.0.0/alternativa/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
17
Alternativa3D5/5.0.0/alternativa/engine3d/.svn/all-wcprops
Normal file
17
Alternativa3D5/5.0.0/alternativa/engine3d/.svn/all-wcprops
Normal file
@@ -0,0 +1,17 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 90
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d
|
||||
END
|
||||
alternativa3d.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 107
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/alternativa3d.as
|
||||
END
|
||||
Alternativa3DVersion.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 114
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/Alternativa3DVersion.as
|
||||
END
|
||||
76
Alternativa3D5/5.0.0/alternativa/engine3d/.svn/entries
Normal file
76
Alternativa3D5/5.0.0/alternativa/engine3d/.svn/entries
Normal file
@@ -0,0 +1,76 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
materials
|
||||
dir
|
||||
|
||||
physics
|
||||
dir
|
||||
|
||||
alternativa3d.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
223b93b983435c6892dde203f8f6eb63
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
display
|
||||
dir
|
||||
|
||||
controllers
|
||||
dir
|
||||
|
||||
core
|
||||
dir
|
||||
|
||||
loaders
|
||||
dir
|
||||
|
||||
Alternativa3DVersion.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
95291b073e3f82e0f5f06949f4c9a13d
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
primitives
|
||||
dir
|
||||
|
||||
errors
|
||||
dir
|
||||
|
||||
1
Alternativa3D5/5.0.0/alternativa/engine3d/.svn/format
Normal file
1
Alternativa3D5/5.0.0/alternativa/engine3d/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
@@ -0,0 +1,26 @@
|
||||
package alternativa.engine3d {
|
||||
|
||||
/**
|
||||
* Класс содержит информацию о версии библиотеки Alternativa3D
|
||||
*/
|
||||
public class Alternativa3DVersion {
|
||||
|
||||
/**
|
||||
* Основная версия библиотеки
|
||||
*/
|
||||
public static const MAJOR_VERSION:uint = 0;
|
||||
/**
|
||||
* Промежуточная версия библиотеки
|
||||
*/
|
||||
public static const MINOR_VERSION:uint = 0;
|
||||
/**
|
||||
* Строковое представление версии библиотеки
|
||||
*/
|
||||
public static const VERSION:String = "5." + MAJOR_VERSION + "." + MINOR_VERSION;
|
||||
|
||||
/**
|
||||
* Полное описание версии
|
||||
*/
|
||||
public static const FULL_VERSION:String = "Alternativa3D " + VERSION;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package alternativa.engine3d {
|
||||
public namespace alternativa3d = "http://3d.alternativaplatform.com";
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package alternativa.engine3d {
|
||||
|
||||
/**
|
||||
* Класс содержит информацию о версии библиотеки Alternativa3D
|
||||
*/
|
||||
public class Alternativa3DVersion {
|
||||
|
||||
/**
|
||||
* Основная версия библиотеки
|
||||
*/
|
||||
public static const MAJOR_VERSION:uint = 0;
|
||||
/**
|
||||
* Промежуточная версия библиотеки
|
||||
*/
|
||||
public static const MINOR_VERSION:uint = 0;
|
||||
/**
|
||||
* Строковое представление версии библиотеки
|
||||
*/
|
||||
public static const VERSION:String = "5." + MAJOR_VERSION + "." + MINOR_VERSION;
|
||||
|
||||
/**
|
||||
* Полное описание версии
|
||||
*/
|
||||
public static const FULL_VERSION:String = "Alternativa3D " + VERSION;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package alternativa.engine3d {
|
||||
public namespace alternativa3d = "http://3d.alternativaplatform.com";
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 102
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/controllers
|
||||
END
|
||||
CameraController.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 122
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/controllers/CameraController.as
|
||||
END
|
||||
@@ -0,0 +1,40 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/controllers
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
CameraController.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
246ca2d8799dd9e364793c81c1f9941d
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
8
|
||||
@@ -0,0 +1,752 @@
|
||||
package alternativa.engine3d.controllers {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.physics.SphereCollider;
|
||||
import alternativa.types.Map;
|
||||
import alternativa.types.Matrix3D;
|
||||
import alternativa.types.Point3D;
|
||||
import alternativa.utils.KeyboardUtils;
|
||||
import alternativa.utils.MathUtils;
|
||||
|
||||
import flash.display.DisplayObject;
|
||||
import flash.events.KeyboardEvent;
|
||||
import flash.events.MouseEvent;
|
||||
import flash.geom.Point;
|
||||
import flash.utils.getTimer;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Контроллер камеры. Контроллер обеспечивает управление движением и поворотами камеры с использованием
|
||||
* клавиатуры и мыши, а также реализует простую проверку столкновений камеры с объектами сцены.
|
||||
*/
|
||||
public class CameraController {
|
||||
/**
|
||||
* Имя действия для привязки клавиш движения вперёд.
|
||||
*/
|
||||
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_ROLL_LEFT:String = "ACTION_ROLL_LEFT";
|
||||
// public static const ACTION_ROLL_RIGHT:String = "ACTION_ROLL_RIGHT";
|
||||
/**
|
||||
* Имя действия для привязки клавиш увеличения угла тангажа.
|
||||
*/
|
||||
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";
|
||||
|
||||
// флаги действий
|
||||
private var _forward:Boolean;
|
||||
private var _back:Boolean;
|
||||
private var _left:Boolean;
|
||||
private var _right:Boolean;
|
||||
private var _up:Boolean;
|
||||
private var _down:Boolean;
|
||||
private var _pitchUp:Boolean;
|
||||
private var _pitchDown:Boolean;
|
||||
private var _yawLeft:Boolean;
|
||||
private var _yawRight:Boolean;
|
||||
|
||||
private var _moveLocal:Boolean = true;
|
||||
|
||||
// Флаг включения управления камерой
|
||||
private var _controlsEnabled:Boolean = false;
|
||||
|
||||
// Значение таймера в начале прошлого кадра
|
||||
private var lastFrameTime:uint;
|
||||
|
||||
// Чувствительность обзора мышью. Коэффициент умножения базовых коэффициентов поворотов.
|
||||
private var _mouseSensitivity:Number = 1;
|
||||
// Коэффициент поворота камеры по тангажу. Значение угла в радианах на один пиксель перемещения мыши по вертикали.
|
||||
private var _mousePitch:Number = Math.PI / 360;
|
||||
// Результирующий коэффициент поворота камеры мышью по тангажу
|
||||
private var _mousePitchCoeff:Number = _mouseSensitivity * _mousePitch;
|
||||
// Коэффициент поворота камеры по рысканью. Значение угла в радианах на один пиксель перемещения мыши по горизонтали.
|
||||
private var _mouseYaw:Number = Math.PI / 360;
|
||||
// Результирующий коэффициент поворота камеры мышью по расканью
|
||||
private var _mouseYawCoeff:Number = _mouseSensitivity * _mouseYaw;
|
||||
|
||||
// Вспомогательные переменные для обзора мышью
|
||||
private var mouseLookActive:Boolean;
|
||||
private var startDragCoords:Point = new Point();
|
||||
private var currentDragCoords:Point = new Point();
|
||||
private var prevDragCoords:Point = new Point();
|
||||
private var startRotX:Number;
|
||||
private var startRotZ:Number;
|
||||
|
||||
// Скорость изменения тангажа в радианах за секунду при управлении с клавиатуры
|
||||
private var _pitchSpeed:Number = 1;
|
||||
// Скорость изменения рысканья в радианах за секунду при управлении с клавиатуры
|
||||
private var _yawSpeed:Number = 1;
|
||||
// Скорость изменения крена в радианах за секунду при управлении с клавиатуры
|
||||
// public var bankSpeed:Number = 2;
|
||||
// private var bankMatrix:Matrix3D = new Matrix3D();
|
||||
|
||||
// Скорость поступательного движения в единицах за секунду
|
||||
private var _speed:Number = 100;
|
||||
|
||||
private var velocity:Point3D = new Point3D();
|
||||
private var destination:Point3D = new Point3D();
|
||||
|
||||
private var _fovStep:Number = Math.PI / 180;
|
||||
private var _zoomMultiplier:Number = 0.1;
|
||||
|
||||
// Привязка клавиш к действиям
|
||||
private var keyBindings:Map = new Map();
|
||||
// Привязка действий к обработчикам
|
||||
private var actionBindings:Map = new Map();
|
||||
|
||||
// Источник событий клавиатуры и мыши
|
||||
private var _eventsSource:DisplayObject;
|
||||
// Управляемая камера
|
||||
private var _camera:Camera3D;
|
||||
|
||||
// Класс реализации определния столкновений
|
||||
private var _collider:SphereCollider;
|
||||
// Флаг необходимости проверки столкновений
|
||||
private var _checkCollisions:Boolean;
|
||||
// Радиус сферы для определения столкновений
|
||||
private var _collisionRadius:Number = 0;
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param eventsSourceObject объект, используемый для получения событий мыши и клавиатуры
|
||||
*/
|
||||
public function CameraController(eventsSourceObject:DisplayObject) {
|
||||
if (eventsSourceObject == null) {
|
||||
throw new ArgumentError("CameraController: eventsSource is null");
|
||||
}
|
||||
_eventsSource = eventsSourceObject;
|
||||
|
||||
actionBindings[ACTION_FORWARD] = setForwardFlag;
|
||||
actionBindings[ACTION_BACK] = setBackFlag;
|
||||
actionBindings[ACTION_LEFT] = setLeftFlag;
|
||||
actionBindings[ACTION_RIGHT] = setRightFlag;
|
||||
actionBindings[ACTION_UP] = setUpFlag;
|
||||
actionBindings[ACTION_DOWN] = setDownFlag;
|
||||
actionBindings[ACTION_PITCH_UP] = setPitchUpFlag;
|
||||
actionBindings[ACTION_PITCH_DOWN] = setPitchDownFlag;
|
||||
actionBindings[ACTION_YAW_LEFT] = setYawLeftFlag;
|
||||
actionBindings[ACTION_YAW_RIGHT] = setYawRightFlag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Установка привязки клавиш по умолчанию.
|
||||
*/
|
||||
public function setDefaultBindings():void {
|
||||
unbindAll();
|
||||
bindKey(KeyboardUtils.W, ACTION_FORWARD);
|
||||
bindKey(KeyboardUtils.S, ACTION_BACK);
|
||||
bindKey(KeyboardUtils.A, ACTION_LEFT);
|
||||
bindKey(KeyboardUtils.D, ACTION_RIGHT);
|
||||
bindKey(KeyboardUtils.SPACE, ACTION_UP);
|
||||
bindKey(KeyboardUtils.SHIFT, ACTION_DOWN);
|
||||
bindKey(KeyboardUtils.UP, ACTION_PITCH_UP);
|
||||
bindKey(KeyboardUtils.DOWN, ACTION_PITCH_DOWN);
|
||||
bindKey(KeyboardUtils.LEFT, ACTION_YAW_LEFT);
|
||||
bindKey(KeyboardUtils.RIGHT, ACTION_YAW_RIGHT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Направление камеры на точку.
|
||||
*
|
||||
* @param point координаты точки направления камеры
|
||||
*/
|
||||
public function lookAt(point:Point3D):void {
|
||||
var dx:Number = point.x - _camera.x;
|
||||
var dy:Number = point.y - _camera.y;
|
||||
var dz:Number = point.z - _camera.z;
|
||||
_camera.rotationZ = -Math.atan2(dx, dy);
|
||||
_camera.rotationX = Math.atan2(dz, Math.sqrt(dx * dx + dy * dy)) - MathUtils.DEG90;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Источник событий клавиатуры и мыши.
|
||||
*/
|
||||
public function get eventsSource():DisplayObject {
|
||||
return _eventsSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set eventsSource(value:DisplayObject):void {
|
||||
if (value == null) {
|
||||
throw new ArgumentError("CameraController: eventsSource is null");
|
||||
}
|
||||
if (_eventsSource != value) {
|
||||
if (_controlsEnabled) {
|
||||
unregisterEventsListeners();
|
||||
}
|
||||
_eventsSource = value;
|
||||
if (_controlsEnabled) {
|
||||
registerEventListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Текущая управляемая камера.
|
||||
*/
|
||||
public function get camera():Camera3D {
|
||||
return _camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set camera(value:Camera3D):void {
|
||||
if (value == null) {
|
||||
controlsEnabled = false;
|
||||
}
|
||||
_camera = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Режим движения камеры. Если значение равно <code>true</code>, то перемещения камеры происходят относительно
|
||||
* локальной системы координат, иначе относительно глобальной.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
public function get moveLocal():Boolean {
|
||||
return _moveLocal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set moveLocal(value:Boolean):void {
|
||||
_moveLocal = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Включение режима проверки столкновений. Установка значения невозможна, пока камера не добавлена на сцену.
|
||||
*/
|
||||
public function get checkCollisions():Boolean {
|
||||
return _checkCollisions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set checkCollisions(value:Boolean):void {
|
||||
if (_camera.scene == null) {
|
||||
return;
|
||||
}
|
||||
if (_checkCollisions != value) {
|
||||
_checkCollisions = value;
|
||||
if (_checkCollisions && _collider == null) {
|
||||
_collider = new SphereCollider(_camera.scene, _collisionRadius);
|
||||
_collider.offsetThreshold = 0.01;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Радиус сферы для определения столкновений.
|
||||
*
|
||||
* @default 0
|
||||
*/
|
||||
public function get collisionRadius():Number {
|
||||
return _collisionRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set collisionRadius(value:Number):void {
|
||||
_collisionRadius = value;
|
||||
if (_collider != null) {
|
||||
_collider.sphereRadius = _collisionRadius;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Связывание клавиши с действием.
|
||||
*
|
||||
* @param keyCode код клавиши
|
||||
* @param action наименование действия
|
||||
*/
|
||||
public function bindKey(keyCode:uint, action:String):void {
|
||||
var method:Function = actionBindings[action];
|
||||
if (method != null) {
|
||||
keyBindings[keyCode] = method;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Очистка привязки клавиши.
|
||||
*
|
||||
* @param keyCode код клавиши
|
||||
*/
|
||||
public function unbindKey(keyCode:uint):void {
|
||||
keyBindings.remove(keyCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Очистка привязки всех клавиш.
|
||||
*/
|
||||
public function unbindAll():void {
|
||||
keyBindings.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setForwardFlag(value:Boolean):void {
|
||||
_forward = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setBackFlag(value:Boolean):void {
|
||||
_back = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setLeftFlag(value:Boolean):void {
|
||||
_left = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setRightFlag(value:Boolean):void {
|
||||
_right = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setUpFlag(value:Boolean):void {
|
||||
_up = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setDownFlag(value:Boolean):void {
|
||||
_down = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setPitchUpFlag(value:Boolean):void {
|
||||
_pitchUp = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setPitchDownFlag(value:Boolean):void {
|
||||
_pitchDown = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setYawLeftFlag(value:Boolean):void {
|
||||
_yawLeft = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setYawRightFlag(value:Boolean):void {
|
||||
_yawRight = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Чувствительность мыши.
|
||||
*
|
||||
* @default 1
|
||||
*/
|
||||
public function get mouseSensitivity():Number {
|
||||
return _mouseSensitivity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set mouseSensitivity(sensitivity:Number):void {
|
||||
_mouseSensitivity = sensitivity;
|
||||
_mousePitchCoeff = _mouseSensitivity * _mousePitch;
|
||||
_mouseYawCoeff = _mouseSensitivity * _mouseYaw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Скорость изменения тангажа при управлении мышью (радианы на пиксель).
|
||||
*
|
||||
* @default Math.PI / 360
|
||||
*/
|
||||
public function get mousePitch():Number {
|
||||
return _mousePitch;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set mousePitch(pitch:Number):void {
|
||||
_mousePitch = pitch;
|
||||
_mousePitchCoeff = _mouseSensitivity * _mousePitch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Скорость изменения рысканья при управлении мышью (радианы на пиксель).
|
||||
*
|
||||
* @default Math.PI / 360
|
||||
*/
|
||||
public function get mouseYaw():Number {
|
||||
return _mouseYaw;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set mouseYaw(yaw:Number):void {
|
||||
_mouseYaw = yaw;
|
||||
_mouseYawCoeff = _mouseSensitivity * _mouseYaw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Угловая скорость по тангажу при управлении с клавиатуры (радианы в секунду).
|
||||
*
|
||||
* @default 1
|
||||
*/
|
||||
public function get pitchSpeed():Number {
|
||||
return _pitchSpeed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set pitchSpeed(spd:Number):void {
|
||||
_pitchSpeed = spd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Угловая скорость по рысканью при управлении с клавиатуры (радианы в секунду).
|
||||
*
|
||||
* @default 1
|
||||
*/
|
||||
public function get yawSpeed():Number {
|
||||
return _yawSpeed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set yawSpeed(spd:Number):void {
|
||||
_yawSpeed = spd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Скорость поступательного движения (единицы в секунду).
|
||||
*/
|
||||
public function get speed():Number {
|
||||
return _speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set speed(spd:Number):void {
|
||||
_speed = spd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Активность управления камеры.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
public function get controlsEnabled():Boolean {
|
||||
return _controlsEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set controlsEnabled(state:Boolean):void {
|
||||
if (_camera == null || _controlsEnabled == state) return;
|
||||
if (state) {
|
||||
lastFrameTime = getTimer();
|
||||
registerEventListeners();
|
||||
}
|
||||
else {
|
||||
unregisterEventsListeners();
|
||||
}
|
||||
_controlsEnabled = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Базовый шаг изменения угла зрения в радианах. Реальный шаг получаеся умножением этого значения на величину
|
||||
* <code>MouseEvent.delta</code>.
|
||||
*
|
||||
* @default Math.PI / 180
|
||||
*/
|
||||
public function get fovStep():Number {
|
||||
return _fovStep;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set fovStep(value:Number):void {
|
||||
_fovStep = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Коэффициент изменения коэффициента увеличения.
|
||||
*
|
||||
* @default 0.1
|
||||
*/
|
||||
public function get zoomMultiplier():Number {
|
||||
return _zoomMultiplier;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set zoomMultiplier(value:Number):void {
|
||||
_zoomMultiplier = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function registerEventListeners():void {
|
||||
_eventsSource.addEventListener(KeyboardEvent.KEY_DOWN, onKey);
|
||||
_eventsSource.addEventListener(KeyboardEvent.KEY_UP, onKey);
|
||||
_eventsSource.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
||||
_eventsSource.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function unregisterEventsListeners():void {
|
||||
_eventsSource.removeEventListener(KeyboardEvent.KEY_DOWN, onKey);
|
||||
_eventsSource.removeEventListener(KeyboardEvent.KEY_UP, onKey);
|
||||
_eventsSource.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
||||
_eventsSource.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||
_eventsSource.removeEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param e
|
||||
*/
|
||||
private function onKey(e:KeyboardEvent):void {
|
||||
var method:Function = keyBindings[e.keyCode];
|
||||
if (method != null) {
|
||||
method.call(this, e.type == KeyboardEvent.KEY_DOWN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param e
|
||||
*/
|
||||
private function onMouseDown(e:MouseEvent):void {
|
||||
mouseLookActive = true;
|
||||
currentDragCoords.x = startDragCoords.x = _eventsSource.stage.mouseX;
|
||||
currentDragCoords.y = startDragCoords.y = _eventsSource.stage.mouseY;
|
||||
startRotX = _camera.rotationX;
|
||||
startRotZ = _camera.rotationZ;
|
||||
_eventsSource.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param e
|
||||
*/
|
||||
private function onMouseUp(e:MouseEvent):void {
|
||||
mouseLookActive = false;
|
||||
_eventsSource.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param e
|
||||
*/
|
||||
private function onMouseWheel(e:MouseEvent):void {
|
||||
if (_camera.orthographic) {
|
||||
_camera.zoom = _camera.zoom * (1 + e.delta * _zoomMultiplier);
|
||||
} else {
|
||||
_camera.fov -= _fovStep * e.delta;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработка управляющих воздействий.
|
||||
* Метод должен вызываться каждый кадр перед вызовом <code>Scene3D.calculate()</code>.
|
||||
*
|
||||
* @see alternativa.engine3d.core.Scene3D#calculate()
|
||||
*/
|
||||
public function processInput(): void {
|
||||
if (!_controlsEnabled || _camera == null) return;
|
||||
|
||||
// Время в секундах от начала предыдущего кадра
|
||||
var frameTime:Number = getTimer() - lastFrameTime;
|
||||
lastFrameTime += frameTime;
|
||||
frameTime /= 1000;
|
||||
|
||||
// Обработка mouselook
|
||||
if (mouseLookActive) {
|
||||
prevDragCoords.x = currentDragCoords.x;
|
||||
prevDragCoords.y = currentDragCoords.y;
|
||||
currentDragCoords.x = _eventsSource.stage.mouseX;
|
||||
currentDragCoords.y = _eventsSource.stage.mouseY;
|
||||
if (!prevDragCoords.equals(currentDragCoords)) {
|
||||
_camera.rotationZ = startRotZ + (startDragCoords.x - currentDragCoords.x) * _mouseYawCoeff;
|
||||
var rotX:Number = startRotX + (startDragCoords.y - currentDragCoords.y) * _mousePitchCoeff;
|
||||
_camera.rotationX = (rotX > 0) ? 0 : (rotX < -MathUtils.DEG180) ? -MathUtils.DEG180 : rotX;
|
||||
}
|
||||
}
|
||||
|
||||
// Поворот относительно вертикальной оси (рысканье, yaw)
|
||||
if (_yawLeft) {
|
||||
_camera.rotationZ += _yawSpeed * frameTime;
|
||||
} else if (_yawRight) {
|
||||
_camera.rotationZ -= _yawSpeed * frameTime;
|
||||
}
|
||||
|
||||
// Поворот относительно поперечной оси (тангаж, pitch)
|
||||
if (_pitchUp) {
|
||||
_camera.rotationX += _pitchSpeed * frameTime;
|
||||
} else if (_pitchDown) {
|
||||
_camera.rotationX -= _pitchSpeed * frameTime;
|
||||
}
|
||||
|
||||
// TODO: Поворот относительно продольной оси (крен, roll)
|
||||
|
||||
var frameDistance:Number = _speed * frameTime;
|
||||
velocity.x = 0;
|
||||
velocity.y = 0;
|
||||
velocity.z = 0;
|
||||
var transformation:Matrix3D = _camera.transformation;
|
||||
|
||||
if (_moveLocal) {
|
||||
// Режим относительных пермещений
|
||||
// Движение вперед-назад
|
||||
if (_forward) {
|
||||
velocity.x += frameDistance * transformation.c;
|
||||
velocity.y += frameDistance * transformation.g;
|
||||
velocity.z += frameDistance * transformation.k;
|
||||
} else if (_back) {
|
||||
velocity.x -= frameDistance * transformation.c;
|
||||
velocity.y -= frameDistance * transformation.g;
|
||||
velocity.z -= frameDistance * transformation.k;
|
||||
}
|
||||
// Движение влево-вправо
|
||||
if (_left) {
|
||||
velocity.x -= frameDistance * transformation.a;
|
||||
velocity.y -= frameDistance * transformation.e;
|
||||
velocity.z -= frameDistance * transformation.i;
|
||||
} else if (_right) {
|
||||
velocity.x += frameDistance * transformation.a;
|
||||
velocity.y += frameDistance * transformation.e;
|
||||
velocity.z += frameDistance * transformation.i;
|
||||
}
|
||||
// Движение вверх-вниз
|
||||
if (_up) {
|
||||
velocity.x -= frameDistance * transformation.b;
|
||||
velocity.y -= frameDistance * transformation.f;
|
||||
velocity.z -= frameDistance * transformation.j;
|
||||
} else if (_down) {
|
||||
velocity.x += frameDistance * transformation.b;
|
||||
velocity.y += frameDistance * transformation.f;
|
||||
velocity.z += frameDistance * transformation.j;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Режим глобальных перемещений
|
||||
var cosZ:Number = Math.cos(_camera.rotationZ);
|
||||
var sinZ:Number = Math.sin(_camera.rotationZ);
|
||||
// Движение вперед-назад
|
||||
if (_forward) {
|
||||
velocity.x -= frameDistance * sinZ;
|
||||
velocity.y += frameDistance * cosZ;
|
||||
} else if (_back) {
|
||||
velocity.x += frameDistance * sinZ;
|
||||
velocity.y -= frameDistance * cosZ;
|
||||
}
|
||||
// Движение влево-вправо
|
||||
if (_left) {
|
||||
velocity.x -= frameDistance * cosZ;
|
||||
velocity.y -= frameDistance * sinZ;
|
||||
} else if (_right) {
|
||||
velocity.x += frameDistance * cosZ;
|
||||
velocity.y += frameDistance * sinZ;
|
||||
}
|
||||
// Движение вверх-вниз
|
||||
if (_up) {
|
||||
velocity.z += frameDistance;
|
||||
} else if (_down) {
|
||||
velocity.z -= frameDistance;
|
||||
}
|
||||
}
|
||||
|
||||
// Коррекция модуля вектора скорости
|
||||
if (velocity.x != 0 || velocity.y != 0 || velocity.z != 0) {
|
||||
velocity.length = frameDistance;
|
||||
}
|
||||
|
||||
if (_checkCollisions) {
|
||||
_collider.calculateDestination(_camera.coords, velocity, destination);
|
||||
_camera.x = destination.x;
|
||||
_camera.y = destination.y;
|
||||
_camera.z = destination.z;
|
||||
} else {
|
||||
_camera.x += velocity.x;
|
||||
_camera.y += velocity.y;
|
||||
_camera.z += velocity.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,752 @@
|
||||
package alternativa.engine3d.controllers {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.physics.SphereCollider;
|
||||
import alternativa.types.Map;
|
||||
import alternativa.types.Matrix3D;
|
||||
import alternativa.types.Point3D;
|
||||
import alternativa.utils.KeyboardUtils;
|
||||
import alternativa.utils.MathUtils;
|
||||
|
||||
import flash.display.DisplayObject;
|
||||
import flash.events.KeyboardEvent;
|
||||
import flash.events.MouseEvent;
|
||||
import flash.geom.Point;
|
||||
import flash.utils.getTimer;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Контроллер камеры. Контроллер обеспечивает управление движением и поворотами камеры с использованием
|
||||
* клавиатуры и мыши, а также реализует простую проверку столкновений камеры с объектами сцены.
|
||||
*/
|
||||
public class CameraController {
|
||||
/**
|
||||
* Имя действия для привязки клавиш движения вперёд.
|
||||
*/
|
||||
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_ROLL_LEFT:String = "ACTION_ROLL_LEFT";
|
||||
// public static const ACTION_ROLL_RIGHT:String = "ACTION_ROLL_RIGHT";
|
||||
/**
|
||||
* Имя действия для привязки клавиш увеличения угла тангажа.
|
||||
*/
|
||||
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";
|
||||
|
||||
// флаги действий
|
||||
private var _forward:Boolean;
|
||||
private var _back:Boolean;
|
||||
private var _left:Boolean;
|
||||
private var _right:Boolean;
|
||||
private var _up:Boolean;
|
||||
private var _down:Boolean;
|
||||
private var _pitchUp:Boolean;
|
||||
private var _pitchDown:Boolean;
|
||||
private var _yawLeft:Boolean;
|
||||
private var _yawRight:Boolean;
|
||||
|
||||
private var _moveLocal:Boolean = true;
|
||||
|
||||
// Флаг включения управления камерой
|
||||
private var _controlsEnabled:Boolean = false;
|
||||
|
||||
// Значение таймера в начале прошлого кадра
|
||||
private var lastFrameTime:uint;
|
||||
|
||||
// Чувствительность обзора мышью. Коэффициент умножения базовых коэффициентов поворотов.
|
||||
private var _mouseSensitivity:Number = 1;
|
||||
// Коэффициент поворота камеры по тангажу. Значение угла в радианах на один пиксель перемещения мыши по вертикали.
|
||||
private var _mousePitch:Number = Math.PI / 360;
|
||||
// Результирующий коэффициент поворота камеры мышью по тангажу
|
||||
private var _mousePitchCoeff:Number = _mouseSensitivity * _mousePitch;
|
||||
// Коэффициент поворота камеры по рысканью. Значение угла в радианах на один пиксель перемещения мыши по горизонтали.
|
||||
private var _mouseYaw:Number = Math.PI / 360;
|
||||
// Результирующий коэффициент поворота камеры мышью по расканью
|
||||
private var _mouseYawCoeff:Number = _mouseSensitivity * _mouseYaw;
|
||||
|
||||
// Вспомогательные переменные для обзора мышью
|
||||
private var mouseLookActive:Boolean;
|
||||
private var startDragCoords:Point = new Point();
|
||||
private var currentDragCoords:Point = new Point();
|
||||
private var prevDragCoords:Point = new Point();
|
||||
private var startRotX:Number;
|
||||
private var startRotZ:Number;
|
||||
|
||||
// Скорость изменения тангажа в радианах за секунду при управлении с клавиатуры
|
||||
private var _pitchSpeed:Number = 1;
|
||||
// Скорость изменения рысканья в радианах за секунду при управлении с клавиатуры
|
||||
private var _yawSpeed:Number = 1;
|
||||
// Скорость изменения крена в радианах за секунду при управлении с клавиатуры
|
||||
// public var bankSpeed:Number = 2;
|
||||
// private var bankMatrix:Matrix3D = new Matrix3D();
|
||||
|
||||
// Скорость поступательного движения в единицах за секунду
|
||||
private var _speed:Number = 100;
|
||||
|
||||
private var velocity:Point3D = new Point3D();
|
||||
private var destination:Point3D = new Point3D();
|
||||
|
||||
private var _fovStep:Number = Math.PI / 180;
|
||||
private var _zoomMultiplier:Number = 0.1;
|
||||
|
||||
// Привязка клавиш к действиям
|
||||
private var keyBindings:Map = new Map();
|
||||
// Привязка действий к обработчикам
|
||||
private var actionBindings:Map = new Map();
|
||||
|
||||
// Источник событий клавиатуры и мыши
|
||||
private var _eventsSource:DisplayObject;
|
||||
// Управляемая камера
|
||||
private var _camera:Camera3D;
|
||||
|
||||
// Класс реализации определния столкновений
|
||||
private var _collider:SphereCollider;
|
||||
// Флаг необходимости проверки столкновений
|
||||
private var _checkCollisions:Boolean;
|
||||
// Радиус сферы для определения столкновений
|
||||
private var _collisionRadius:Number = 0;
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param eventsSourceObject объект, используемый для получения событий мыши и клавиатуры
|
||||
*/
|
||||
public function CameraController(eventsSourceObject:DisplayObject) {
|
||||
if (eventsSourceObject == null) {
|
||||
throw new ArgumentError("CameraController: eventsSource is null");
|
||||
}
|
||||
_eventsSource = eventsSourceObject;
|
||||
|
||||
actionBindings[ACTION_FORWARD] = setForwardFlag;
|
||||
actionBindings[ACTION_BACK] = setBackFlag;
|
||||
actionBindings[ACTION_LEFT] = setLeftFlag;
|
||||
actionBindings[ACTION_RIGHT] = setRightFlag;
|
||||
actionBindings[ACTION_UP] = setUpFlag;
|
||||
actionBindings[ACTION_DOWN] = setDownFlag;
|
||||
actionBindings[ACTION_PITCH_UP] = setPitchUpFlag;
|
||||
actionBindings[ACTION_PITCH_DOWN] = setPitchDownFlag;
|
||||
actionBindings[ACTION_YAW_LEFT] = setYawLeftFlag;
|
||||
actionBindings[ACTION_YAW_RIGHT] = setYawRightFlag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Установка привязки клавиш по умолчанию.
|
||||
*/
|
||||
public function setDefaultBindings():void {
|
||||
unbindAll();
|
||||
bindKey(KeyboardUtils.W, ACTION_FORWARD);
|
||||
bindKey(KeyboardUtils.S, ACTION_BACK);
|
||||
bindKey(KeyboardUtils.A, ACTION_LEFT);
|
||||
bindKey(KeyboardUtils.D, ACTION_RIGHT);
|
||||
bindKey(KeyboardUtils.SPACE, ACTION_UP);
|
||||
bindKey(KeyboardUtils.SHIFT, ACTION_DOWN);
|
||||
bindKey(KeyboardUtils.UP, ACTION_PITCH_UP);
|
||||
bindKey(KeyboardUtils.DOWN, ACTION_PITCH_DOWN);
|
||||
bindKey(KeyboardUtils.LEFT, ACTION_YAW_LEFT);
|
||||
bindKey(KeyboardUtils.RIGHT, ACTION_YAW_RIGHT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Направление камеры на точку.
|
||||
*
|
||||
* @param point координаты точки направления камеры
|
||||
*/
|
||||
public function lookAt(point:Point3D):void {
|
||||
var dx:Number = point.x - _camera.x;
|
||||
var dy:Number = point.y - _camera.y;
|
||||
var dz:Number = point.z - _camera.z;
|
||||
_camera.rotationZ = -Math.atan2(dx, dy);
|
||||
_camera.rotationX = Math.atan2(dz, Math.sqrt(dx * dx + dy * dy)) - MathUtils.DEG90;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Источник событий клавиатуры и мыши.
|
||||
*/
|
||||
public function get eventsSource():DisplayObject {
|
||||
return _eventsSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set eventsSource(value:DisplayObject):void {
|
||||
if (value == null) {
|
||||
throw new ArgumentError("CameraController: eventsSource is null");
|
||||
}
|
||||
if (_eventsSource != value) {
|
||||
if (_controlsEnabled) {
|
||||
unregisterEventsListeners();
|
||||
}
|
||||
_eventsSource = value;
|
||||
if (_controlsEnabled) {
|
||||
registerEventListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Текущая управляемая камера.
|
||||
*/
|
||||
public function get camera():Camera3D {
|
||||
return _camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set camera(value:Camera3D):void {
|
||||
if (value == null) {
|
||||
controlsEnabled = false;
|
||||
}
|
||||
_camera = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Режим движения камеры. Если значение равно <code>true</code>, то перемещения камеры происходят относительно
|
||||
* локальной системы координат, иначе относительно глобальной.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
public function get moveLocal():Boolean {
|
||||
return _moveLocal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set moveLocal(value:Boolean):void {
|
||||
_moveLocal = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Включение режима проверки столкновений. Установка значения невозможна, пока камера не добавлена на сцену.
|
||||
*/
|
||||
public function get checkCollisions():Boolean {
|
||||
return _checkCollisions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set checkCollisions(value:Boolean):void {
|
||||
if (_camera.scene == null) {
|
||||
return;
|
||||
}
|
||||
if (_checkCollisions != value) {
|
||||
_checkCollisions = value;
|
||||
if (_checkCollisions && _collider == null) {
|
||||
_collider = new SphereCollider(_camera.scene, _collisionRadius);
|
||||
_collider.offsetThreshold = 0.01;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Радиус сферы для определения столкновений.
|
||||
*
|
||||
* @default 0
|
||||
*/
|
||||
public function get collisionRadius():Number {
|
||||
return _collisionRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set collisionRadius(value:Number):void {
|
||||
_collisionRadius = value;
|
||||
if (_collider != null) {
|
||||
_collider.sphereRadius = _collisionRadius;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Связывание клавиши с действием.
|
||||
*
|
||||
* @param keyCode код клавиши
|
||||
* @param action наименование действия
|
||||
*/
|
||||
public function bindKey(keyCode:uint, action:String):void {
|
||||
var method:Function = actionBindings[action];
|
||||
if (method != null) {
|
||||
keyBindings[keyCode] = method;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Очистка привязки клавиши.
|
||||
*
|
||||
* @param keyCode код клавиши
|
||||
*/
|
||||
public function unbindKey(keyCode:uint):void {
|
||||
keyBindings.remove(keyCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Очистка привязки всех клавиш.
|
||||
*/
|
||||
public function unbindAll():void {
|
||||
keyBindings.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setForwardFlag(value:Boolean):void {
|
||||
_forward = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setBackFlag(value:Boolean):void {
|
||||
_back = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setLeftFlag(value:Boolean):void {
|
||||
_left = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setRightFlag(value:Boolean):void {
|
||||
_right = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setUpFlag(value:Boolean):void {
|
||||
_up = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setDownFlag(value:Boolean):void {
|
||||
_down = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setPitchUpFlag(value:Boolean):void {
|
||||
_pitchUp = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setPitchDownFlag(value:Boolean):void {
|
||||
_pitchDown = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setYawLeftFlag(value:Boolean):void {
|
||||
_yawLeft = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param value
|
||||
*/
|
||||
private function setYawRightFlag(value:Boolean):void {
|
||||
_yawRight = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Чувствительность мыши.
|
||||
*
|
||||
* @default 1
|
||||
*/
|
||||
public function get mouseSensitivity():Number {
|
||||
return _mouseSensitivity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set mouseSensitivity(sensitivity:Number):void {
|
||||
_mouseSensitivity = sensitivity;
|
||||
_mousePitchCoeff = _mouseSensitivity * _mousePitch;
|
||||
_mouseYawCoeff = _mouseSensitivity * _mouseYaw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Скорость изменения тангажа при управлении мышью (радианы на пиксель).
|
||||
*
|
||||
* @default Math.PI / 360
|
||||
*/
|
||||
public function get mousePitch():Number {
|
||||
return _mousePitch;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set mousePitch(pitch:Number):void {
|
||||
_mousePitch = pitch;
|
||||
_mousePitchCoeff = _mouseSensitivity * _mousePitch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Скорость изменения рысканья при управлении мышью (радианы на пиксель).
|
||||
*
|
||||
* @default Math.PI / 360
|
||||
*/
|
||||
public function get mouseYaw():Number {
|
||||
return _mouseYaw;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set mouseYaw(yaw:Number):void {
|
||||
_mouseYaw = yaw;
|
||||
_mouseYawCoeff = _mouseSensitivity * _mouseYaw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Угловая скорость по тангажу при управлении с клавиатуры (радианы в секунду).
|
||||
*
|
||||
* @default 1
|
||||
*/
|
||||
public function get pitchSpeed():Number {
|
||||
return _pitchSpeed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set pitchSpeed(spd:Number):void {
|
||||
_pitchSpeed = spd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Угловая скорость по рысканью при управлении с клавиатуры (радианы в секунду).
|
||||
*
|
||||
* @default 1
|
||||
*/
|
||||
public function get yawSpeed():Number {
|
||||
return _yawSpeed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set yawSpeed(spd:Number):void {
|
||||
_yawSpeed = spd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Скорость поступательного движения (единицы в секунду).
|
||||
*/
|
||||
public function get speed():Number {
|
||||
return _speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set speed(spd:Number):void {
|
||||
_speed = spd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Активность управления камеры.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
public function get controlsEnabled():Boolean {
|
||||
return _controlsEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set controlsEnabled(state:Boolean):void {
|
||||
if (_camera == null || _controlsEnabled == state) return;
|
||||
if (state) {
|
||||
lastFrameTime = getTimer();
|
||||
registerEventListeners();
|
||||
}
|
||||
else {
|
||||
unregisterEventsListeners();
|
||||
}
|
||||
_controlsEnabled = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Базовый шаг изменения угла зрения в радианах. Реальный шаг получаеся умножением этого значения на величину
|
||||
* <code>MouseEvent.delta</code>.
|
||||
*
|
||||
* @default Math.PI / 180
|
||||
*/
|
||||
public function get fovStep():Number {
|
||||
return _fovStep;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set fovStep(value:Number):void {
|
||||
_fovStep = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Коэффициент изменения коэффициента увеличения.
|
||||
*
|
||||
* @default 0.1
|
||||
*/
|
||||
public function get zoomMultiplier():Number {
|
||||
return _zoomMultiplier;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set zoomMultiplier(value:Number):void {
|
||||
_zoomMultiplier = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function registerEventListeners():void {
|
||||
_eventsSource.addEventListener(KeyboardEvent.KEY_DOWN, onKey);
|
||||
_eventsSource.addEventListener(KeyboardEvent.KEY_UP, onKey);
|
||||
_eventsSource.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
||||
_eventsSource.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function unregisterEventsListeners():void {
|
||||
_eventsSource.removeEventListener(KeyboardEvent.KEY_DOWN, onKey);
|
||||
_eventsSource.removeEventListener(KeyboardEvent.KEY_UP, onKey);
|
||||
_eventsSource.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
||||
_eventsSource.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||
_eventsSource.removeEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param e
|
||||
*/
|
||||
private function onKey(e:KeyboardEvent):void {
|
||||
var method:Function = keyBindings[e.keyCode];
|
||||
if (method != null) {
|
||||
method.call(this, e.type == KeyboardEvent.KEY_DOWN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param e
|
||||
*/
|
||||
private function onMouseDown(e:MouseEvent):void {
|
||||
mouseLookActive = true;
|
||||
currentDragCoords.x = startDragCoords.x = _eventsSource.stage.mouseX;
|
||||
currentDragCoords.y = startDragCoords.y = _eventsSource.stage.mouseY;
|
||||
startRotX = _camera.rotationX;
|
||||
startRotZ = _camera.rotationZ;
|
||||
_eventsSource.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param e
|
||||
*/
|
||||
private function onMouseUp(e:MouseEvent):void {
|
||||
mouseLookActive = false;
|
||||
_eventsSource.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param e
|
||||
*/
|
||||
private function onMouseWheel(e:MouseEvent):void {
|
||||
if (_camera.orthographic) {
|
||||
_camera.zoom = _camera.zoom * (1 + e.delta * _zoomMultiplier);
|
||||
} else {
|
||||
_camera.fov -= _fovStep * e.delta;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработка управляющих воздействий.
|
||||
* Метод должен вызываться каждый кадр перед вызовом <code>Scene3D.calculate()</code>.
|
||||
*
|
||||
* @see alternativa.engine3d.core.Scene3D#calculate()
|
||||
*/
|
||||
public function processInput(): void {
|
||||
if (!_controlsEnabled || _camera == null) return;
|
||||
|
||||
// Время в секундах от начала предыдущего кадра
|
||||
var frameTime:Number = getTimer() - lastFrameTime;
|
||||
lastFrameTime += frameTime;
|
||||
frameTime /= 1000;
|
||||
|
||||
// Обработка mouselook
|
||||
if (mouseLookActive) {
|
||||
prevDragCoords.x = currentDragCoords.x;
|
||||
prevDragCoords.y = currentDragCoords.y;
|
||||
currentDragCoords.x = _eventsSource.stage.mouseX;
|
||||
currentDragCoords.y = _eventsSource.stage.mouseY;
|
||||
if (!prevDragCoords.equals(currentDragCoords)) {
|
||||
_camera.rotationZ = startRotZ + (startDragCoords.x - currentDragCoords.x) * _mouseYawCoeff;
|
||||
var rotX:Number = startRotX + (startDragCoords.y - currentDragCoords.y) * _mousePitchCoeff;
|
||||
_camera.rotationX = (rotX > 0) ? 0 : (rotX < -MathUtils.DEG180) ? -MathUtils.DEG180 : rotX;
|
||||
}
|
||||
}
|
||||
|
||||
// Поворот относительно вертикальной оси (рысканье, yaw)
|
||||
if (_yawLeft) {
|
||||
_camera.rotationZ += _yawSpeed * frameTime;
|
||||
} else if (_yawRight) {
|
||||
_camera.rotationZ -= _yawSpeed * frameTime;
|
||||
}
|
||||
|
||||
// Поворот относительно поперечной оси (тангаж, pitch)
|
||||
if (_pitchUp) {
|
||||
_camera.rotationX += _pitchSpeed * frameTime;
|
||||
} else if (_pitchDown) {
|
||||
_camera.rotationX -= _pitchSpeed * frameTime;
|
||||
}
|
||||
|
||||
// TODO: Поворот относительно продольной оси (крен, roll)
|
||||
|
||||
var frameDistance:Number = _speed * frameTime;
|
||||
velocity.x = 0;
|
||||
velocity.y = 0;
|
||||
velocity.z = 0;
|
||||
var transformation:Matrix3D = _camera.transformation;
|
||||
|
||||
if (_moveLocal) {
|
||||
// Режим относительных пермещений
|
||||
// Движение вперед-назад
|
||||
if (_forward) {
|
||||
velocity.x += frameDistance * transformation.c;
|
||||
velocity.y += frameDistance * transformation.g;
|
||||
velocity.z += frameDistance * transformation.k;
|
||||
} else if (_back) {
|
||||
velocity.x -= frameDistance * transformation.c;
|
||||
velocity.y -= frameDistance * transformation.g;
|
||||
velocity.z -= frameDistance * transformation.k;
|
||||
}
|
||||
// Движение влево-вправо
|
||||
if (_left) {
|
||||
velocity.x -= frameDistance * transformation.a;
|
||||
velocity.y -= frameDistance * transformation.e;
|
||||
velocity.z -= frameDistance * transformation.i;
|
||||
} else if (_right) {
|
||||
velocity.x += frameDistance * transformation.a;
|
||||
velocity.y += frameDistance * transformation.e;
|
||||
velocity.z += frameDistance * transformation.i;
|
||||
}
|
||||
// Движение вверх-вниз
|
||||
if (_up) {
|
||||
velocity.x -= frameDistance * transformation.b;
|
||||
velocity.y -= frameDistance * transformation.f;
|
||||
velocity.z -= frameDistance * transformation.j;
|
||||
} else if (_down) {
|
||||
velocity.x += frameDistance * transformation.b;
|
||||
velocity.y += frameDistance * transformation.f;
|
||||
velocity.z += frameDistance * transformation.j;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Режим глобальных перемещений
|
||||
var cosZ:Number = Math.cos(_camera.rotationZ);
|
||||
var sinZ:Number = Math.sin(_camera.rotationZ);
|
||||
// Движение вперед-назад
|
||||
if (_forward) {
|
||||
velocity.x -= frameDistance * sinZ;
|
||||
velocity.y += frameDistance * cosZ;
|
||||
} else if (_back) {
|
||||
velocity.x += frameDistance * sinZ;
|
||||
velocity.y -= frameDistance * cosZ;
|
||||
}
|
||||
// Движение влево-вправо
|
||||
if (_left) {
|
||||
velocity.x -= frameDistance * cosZ;
|
||||
velocity.y -= frameDistance * sinZ;
|
||||
} else if (_right) {
|
||||
velocity.x += frameDistance * cosZ;
|
||||
velocity.y += frameDistance * sinZ;
|
||||
}
|
||||
// Движение вверх-вниз
|
||||
if (_up) {
|
||||
velocity.z += frameDistance;
|
||||
} else if (_down) {
|
||||
velocity.z -= frameDistance;
|
||||
}
|
||||
}
|
||||
|
||||
// Коррекция модуля вектора скорости
|
||||
if (velocity.x != 0 || velocity.y != 0 || velocity.z != 0) {
|
||||
velocity.length = frameDistance;
|
||||
}
|
||||
|
||||
if (_checkCollisions) {
|
||||
_collider.calculateDestination(_camera.coords, velocity, destination);
|
||||
_camera.x = destination.x;
|
||||
_camera.y = destination.y;
|
||||
_camera.z = destination.z;
|
||||
} else {
|
||||
_camera.x += velocity.x;
|
||||
_camera.y += velocity.y;
|
||||
_camera.z += velocity.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 95
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/core
|
||||
END
|
||||
Object3D.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 107
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/core/Object3D.as
|
||||
END
|
||||
Vertex.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 105
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/core/Vertex.as
|
||||
END
|
||||
Face.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 103
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/core/Face.as
|
||||
END
|
||||
Camera3D.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 107
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/core/Camera3D.as
|
||||
END
|
||||
Operation.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 108
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/core/Operation.as
|
||||
END
|
||||
Scene3D.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 106
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/core/Scene3D.as
|
||||
END
|
||||
Surface.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 106
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/core/Surface.as
|
||||
END
|
||||
BSPNode.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 106
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/core/BSPNode.as
|
||||
END
|
||||
Mesh.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 103
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/core/Mesh.as
|
||||
END
|
||||
PolyPrimitive.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 112
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/core/PolyPrimitive.as
|
||||
END
|
||||
148
Alternativa3D5/5.0.0/alternativa/engine3d/core/.svn/entries
Normal file
148
Alternativa3D5/5.0.0/alternativa/engine3d/core/.svn/entries
Normal file
@@ -0,0 +1,148 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/core
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
Object3D.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
c3b15bc67d64e3b1df17d62cfdd90d4e
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
Vertex.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
78730330863ca32a55b4143e316834ba
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
Face.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
8a7d06290187c16cb65b509a2d9ce67b
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
Camera3D.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
00a6b4d1996a6827ec05c32f827d06e5
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
Operation.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
31a3b706c87346f4d45a2aa783b38c9f
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
Scene3D.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
fc3607db2f2d3c4d7c826ed14ad7bc50
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
Surface.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
fbb82c3b8d3d03128dbb7413c0e30881
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
BSPNode.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
e252b1e950354fdb596a11c374363487
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
Mesh.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
71906cfd52516e5ce44e7f9e6c1ffe6c
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
PolyPrimitive.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
94e9cde4c8ab63421813ff617019a62e
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
8
|
||||
@@ -0,0 +1,65 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.types.Point3D;
|
||||
import alternativa.types.Set;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public final class BSPNode {
|
||||
|
||||
// Родительская нода
|
||||
alternativa3d var parent:BSPNode;
|
||||
|
||||
// Дочерние ветки
|
||||
alternativa3d var front:BSPNode;
|
||||
alternativa3d var back:BSPNode;
|
||||
|
||||
// Нормаль плоскости ноды
|
||||
alternativa3d var normal:Point3D = new Point3D();
|
||||
|
||||
// Смещение плоскости примитива
|
||||
alternativa3d var offset:Number;
|
||||
|
||||
// Минимальная мобильность ноды
|
||||
alternativa3d var mobility:int = int.MAX_VALUE;
|
||||
|
||||
// Набор примитивов в ноде
|
||||
alternativa3d var primitive:PolyPrimitive;
|
||||
alternativa3d var backPrimitives:Set;
|
||||
alternativa3d var frontPrimitives:Set;
|
||||
|
||||
// Хранилище неиспользуемых нод
|
||||
static private var collector:Array = new Array();
|
||||
|
||||
// Создать ноду на основе примитива
|
||||
static alternativa3d function createBSPNode(primitive:PolyPrimitive):BSPNode {
|
||||
// Достаём ноду из коллектора
|
||||
var node:BSPNode = collector.pop();
|
||||
// Если коллектор пуст, создаём новую ноду
|
||||
if (node == null) {
|
||||
node = new BSPNode();
|
||||
}
|
||||
|
||||
// Добавляем примитив в ноду
|
||||
node.primitive = primitive;
|
||||
// Сохраняем ноду
|
||||
primitive.node = node;
|
||||
// Сохраняем плоскость
|
||||
node.normal.copy(primitive.face.globalNormal);
|
||||
node.offset = primitive.face.globalOffset;
|
||||
// Сохраняем мобильность
|
||||
node.mobility = primitive.mobility;
|
||||
return node;
|
||||
}
|
||||
|
||||
// Удалить ноду, все ссылки должны быть почищены
|
||||
static alternativa3d function destroyBSPNode(node:BSPNode):void {
|
||||
//trace(node.back, node.front, node.parent, node.primitive, node.backPrimitives, node.frontPrimitives);
|
||||
collector.push(node);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,892 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.engine3d.display.Skin;
|
||||
import alternativa.engine3d.display.View;
|
||||
import alternativa.engine3d.materials.DrawPoint;
|
||||
import alternativa.engine3d.materials.SurfaceMaterial;
|
||||
import alternativa.types.Matrix3D;
|
||||
import alternativa.types.Point3D;
|
||||
import alternativa.types.Set;
|
||||
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Point;
|
||||
import alternativa.utils.MathUtils;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Камера для отображения 3D-сцены на экране.
|
||||
*/
|
||||
public class Camera3D extends Object3D {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчёт матрицы пространства камеры
|
||||
*/
|
||||
alternativa3d var calculateMatrixOperation:Operation = new Operation("calculateMatrix", this, calculateMatrix, Operation.CAMERA_CALCULATE_MATRIX);
|
||||
/**
|
||||
* @private
|
||||
* Расчёт плоскостей отсечения
|
||||
*/
|
||||
alternativa3d var calculatePlanesOperation:Operation = new Operation("calculatePlanes", this, calculatePlanes, Operation.CAMERA_CALCULATE_PLANES);
|
||||
/**
|
||||
* @private
|
||||
* Отрисовка
|
||||
*/
|
||||
alternativa3d var renderOperation:Operation = new Operation("render", this, render, Operation.CAMERA_RENDER);
|
||||
|
||||
// Инкремент количества объектов
|
||||
private static var counter:uint = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Поле зрения
|
||||
*/
|
||||
alternativa3d var _fov:Number = Math.PI/2;
|
||||
/**
|
||||
* @private
|
||||
* Фокусное расстояние
|
||||
*/
|
||||
alternativa3d var focalLength:Number;
|
||||
/**
|
||||
* @private
|
||||
* Перспективное искажение
|
||||
*/
|
||||
alternativa3d var focalDistortion:Number;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Флаги рассчитанности UV-матриц
|
||||
*/
|
||||
alternativa3d var uvMatricesCalculated:Set = new Set(true);
|
||||
|
||||
// Всмомогательные точки для расчёта UV-матриц
|
||||
private var textureA:Point3D = new Point3D();
|
||||
private var textureB:Point3D = new Point3D();
|
||||
private var textureC:Point3D = new Point3D();
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Вид из камеры
|
||||
*/
|
||||
alternativa3d var _view:View;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Режим отрисовки
|
||||
*/
|
||||
alternativa3d var _orthographic:Boolean = false;
|
||||
private var fullDraw:Boolean;
|
||||
|
||||
// Масштаб
|
||||
private var _zoom:Number = 1;
|
||||
|
||||
// Синус половинчатого угла обзора камеры
|
||||
private var viewAngle:Number;
|
||||
|
||||
// Направление камеры
|
||||
private var direction:Point3D = new Point3D(0, 0, 1);
|
||||
|
||||
// Обратная трансформация камеры
|
||||
private var cameraMatrix:Matrix3D = new Matrix3D();
|
||||
|
||||
// Скины
|
||||
private var firstSkin:Skin;
|
||||
private var prevSkin:Skin;
|
||||
private var currentSkin:Skin;
|
||||
|
||||
// Плоскости отсечения
|
||||
private var leftPlane:Point3D = new Point3D();
|
||||
private var rightPlane:Point3D = new Point3D();
|
||||
private var topPlane:Point3D = new Point3D();
|
||||
private var bottomPlane:Point3D = new Point3D();
|
||||
private var leftOffset:Number;
|
||||
private var rightOffset:Number;
|
||||
private var topOffset:Number;
|
||||
private var bottomOffset:Number;
|
||||
|
||||
// Вспомогательные массивы точек для отрисовки
|
||||
private var points1:Array = new Array();
|
||||
private var points2:Array = new Array();
|
||||
|
||||
/**
|
||||
* Создание нового экземпляра камеры.
|
||||
*
|
||||
* @param name наименование камеры
|
||||
*/
|
||||
public function Camera3D(name:String = null) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function calculateMatrix():void {
|
||||
// Расчёт матрицы пространства камеры
|
||||
cameraMatrix.copy(transformation);
|
||||
cameraMatrix.invert();
|
||||
if (_orthographic) {
|
||||
cameraMatrix.scale(_zoom, _zoom, _zoom);
|
||||
}
|
||||
// Направление камеры
|
||||
direction.x = transformation.c;
|
||||
direction.y = transformation.g;
|
||||
direction.z = transformation.k;
|
||||
direction.normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчёт плоскостей отсечения
|
||||
*/
|
||||
private function calculatePlanes():void {
|
||||
var halfWidth:Number = _view._width*0.5;
|
||||
var halfHeight:Number = _view._height*0.5;
|
||||
|
||||
var aw:Number = transformation.a*halfWidth;
|
||||
var ew:Number = transformation.e*halfWidth;
|
||||
var iw:Number = transformation.i*halfWidth;
|
||||
var bh:Number = transformation.b*halfHeight;
|
||||
var fh:Number = transformation.f*halfHeight;
|
||||
var jh:Number = transformation.j*halfHeight;
|
||||
if (_orthographic) {
|
||||
// Расчёт плоскостей отсечения в изометрии
|
||||
aw /= _zoom;
|
||||
ew /= _zoom;
|
||||
iw /= _zoom;
|
||||
bh /= _zoom;
|
||||
fh /= _zoom;
|
||||
jh /= _zoom;
|
||||
|
||||
// Левая плоскость
|
||||
leftPlane.x = transformation.f*transformation.k - transformation.j*transformation.g;
|
||||
leftPlane.y = transformation.j*transformation.c - transformation.b*transformation.k;
|
||||
leftPlane.z = transformation.b*transformation.g - transformation.f*transformation.c;
|
||||
leftOffset = (transformation.d - aw)*leftPlane.x + (transformation.h - ew)*leftPlane.y + (transformation.l - iw)*leftPlane.z;
|
||||
|
||||
// Правая плоскость
|
||||
rightPlane.x = -leftPlane.x;
|
||||
rightPlane.y = -leftPlane.y;
|
||||
rightPlane.z = -leftPlane.z;
|
||||
rightOffset = (transformation.d + aw)*rightPlane.x + (transformation.h + ew)*rightPlane.y + (transformation.l + iw)*rightPlane.z;
|
||||
|
||||
// Верхняя плоскость
|
||||
topPlane.x = transformation.g*transformation.i - transformation.k*transformation.e;
|
||||
topPlane.y = transformation.k*transformation.a - transformation.c*transformation.i;
|
||||
topPlane.z = transformation.c*transformation.e - transformation.g*transformation.a;
|
||||
topOffset = (transformation.d - bh)*topPlane.x + (transformation.h - fh)*topPlane.y + (transformation.l - jh)*topPlane.z;
|
||||
|
||||
// Нижняя плоскость
|
||||
bottomPlane.x = -topPlane.x;
|
||||
bottomPlane.y = -topPlane.y;
|
||||
bottomPlane.z = -topPlane.z;
|
||||
bottomOffset = (transformation.d + bh)*bottomPlane.x + (transformation.h + fh)*bottomPlane.y + (transformation.l + jh)*bottomPlane.z;
|
||||
} else {
|
||||
// Вычисляем расстояние фокуса
|
||||
focalLength = Math.sqrt(_view._width*_view._width + _view._height*_view._height)*0.5/Math.tan(0.5*_fov);
|
||||
// Вычисляем минимальное (однопиксельное) искажение перспективной коррекции
|
||||
focalDistortion = 1/(focalLength*focalLength);
|
||||
|
||||
// Расчёт плоскостей отсечения в перспективе
|
||||
var cl:Number = transformation.c*focalLength;
|
||||
var gl:Number = transformation.g*focalLength;
|
||||
var kl:Number = transformation.k*focalLength;
|
||||
|
||||
// Угловые вектора пирамиды видимости
|
||||
var leftTopX:Number = -aw - bh + cl;
|
||||
var leftTopY:Number = -ew - fh + gl;
|
||||
var leftTopZ:Number = -iw - jh + kl;
|
||||
var rightTopX:Number = aw - bh + cl;
|
||||
var rightTopY:Number = ew - fh + gl;
|
||||
var rightTopZ:Number = iw - jh + kl;
|
||||
var leftBottomX:Number = -aw + bh + cl;
|
||||
var leftBottomY:Number = -ew + fh + gl;
|
||||
var leftBottomZ:Number = -iw + jh + kl;
|
||||
var rightBottomX:Number = aw + bh + cl;
|
||||
var rightBottomY:Number = ew + fh + gl;
|
||||
var rightBottomZ:Number = iw + jh + kl;
|
||||
|
||||
// Левая плоскость
|
||||
leftPlane.x = leftBottomY*leftTopZ - leftBottomZ*leftTopY;
|
||||
leftPlane.y = leftBottomZ*leftTopX - leftBottomX*leftTopZ;
|
||||
leftPlane.z = leftBottomX*leftTopY - leftBottomY*leftTopX;
|
||||
leftOffset = transformation.d*leftPlane.x + transformation.h*leftPlane.y + transformation.l*leftPlane.z;
|
||||
|
||||
// Правая плоскость
|
||||
rightPlane.x = rightTopY*rightBottomZ - rightTopZ*rightBottomY;
|
||||
rightPlane.y = rightTopZ*rightBottomX - rightTopX*rightBottomZ;
|
||||
rightPlane.z = rightTopX*rightBottomY - rightTopY*rightBottomX;
|
||||
rightOffset = transformation.d*rightPlane.x + transformation.h*rightPlane.y + transformation.l*rightPlane.z;
|
||||
|
||||
// Верхняя плоскость
|
||||
topPlane.x = leftTopY*rightTopZ - leftTopZ*rightTopY;
|
||||
topPlane.y = leftTopZ*rightTopX - leftTopX*rightTopZ;
|
||||
topPlane.z = leftTopX*rightTopY - leftTopY*rightTopX;
|
||||
topOffset = transformation.d*topPlane.x + transformation.h*topPlane.y + transformation.l*topPlane.z;
|
||||
|
||||
// Нижняя плоскость
|
||||
bottomPlane.x = rightBottomY*leftBottomZ - rightBottomZ*leftBottomY;
|
||||
bottomPlane.y = rightBottomZ*leftBottomX - rightBottomX*leftBottomZ;
|
||||
bottomPlane.z = rightBottomX*leftBottomY - rightBottomY*leftBottomX;
|
||||
bottomOffset = transformation.d*bottomPlane.x + transformation.h*bottomPlane.y + transformation.l*bottomPlane.z;
|
||||
|
||||
|
||||
// Расчёт угла конуса
|
||||
var length:Number = Math.sqrt(leftTopX*leftTopX + leftTopY*leftTopY + leftTopZ*leftTopZ);
|
||||
leftTopX /= length;
|
||||
leftTopY /= length;
|
||||
leftTopZ /= length;
|
||||
length = Math.sqrt(rightTopX*rightTopX + rightTopY*rightTopY + rightTopZ*rightTopZ);
|
||||
rightTopX /= length;
|
||||
rightTopY /= length;
|
||||
rightTopZ /= length;
|
||||
length = Math.sqrt(leftBottomX*leftBottomX + leftBottomY*leftBottomY + leftBottomZ*leftBottomZ);
|
||||
leftBottomX /= length;
|
||||
leftBottomY /= length;
|
||||
leftBottomZ /= length;
|
||||
length = Math.sqrt(rightBottomX*rightBottomX + rightBottomY*rightBottomY + rightBottomZ*rightBottomZ);
|
||||
rightBottomX /= length;
|
||||
rightBottomY /= length;
|
||||
rightBottomZ /= length;
|
||||
|
||||
viewAngle = leftTopX*direction.x + leftTopY*direction.y + leftTopZ*direction.z;
|
||||
var dot:Number = rightTopX*direction.x + rightTopY*direction.y + rightTopZ*direction.z;
|
||||
viewAngle = (dot < viewAngle) ? dot : viewAngle;
|
||||
dot = leftBottomX*direction.x + leftBottomY*direction.y + leftBottomZ*direction.z;
|
||||
viewAngle = (dot < viewAngle) ? dot : viewAngle;
|
||||
dot = rightBottomX*direction.x + rightBottomY*direction.y + rightBottomZ*direction.z;
|
||||
viewAngle = (dot < viewAngle) ? dot : viewAngle;
|
||||
|
||||
viewAngle = Math.sin(Math.acos(viewAngle));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function render():void {
|
||||
// Режим отрисовки
|
||||
fullDraw = (calculateMatrixOperation.queued || calculatePlanesOperation.queued);
|
||||
|
||||
// Очистка рассчитанных текстурных матриц
|
||||
uvMatricesCalculated.clear();
|
||||
|
||||
// Отрисовка
|
||||
prevSkin = null;
|
||||
currentSkin = firstSkin;
|
||||
renderBSPNode(_scene.bsp);
|
||||
|
||||
// Удаление ненужных скинов
|
||||
while (currentSkin != null) {
|
||||
removeCurrentSkin();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function renderBSPNode(node:BSPNode):void {
|
||||
if (node != null) {
|
||||
var primitive:*;
|
||||
var normal:Point3D = node.normal;
|
||||
var cameraAngle:Number = direction.x*normal.x + direction.y*normal.y + direction.z*normal.z;
|
||||
var cameraOffset:Number;
|
||||
if (!_orthographic) {
|
||||
cameraOffset = globalCoords.x*normal.x + globalCoords.y*normal.y + globalCoords.z*normal.z - node.offset;
|
||||
}
|
||||
if (node.primitive != null) {
|
||||
// В ноде только базовый примитив
|
||||
if (_orthographic ? (cameraAngle < 0) : (cameraOffset > 0)) {
|
||||
// Камера спереди ноды
|
||||
if (_orthographic || cameraAngle < viewAngle) {
|
||||
renderBSPNode(node.back);
|
||||
drawSkin(node.primitive);
|
||||
}
|
||||
renderBSPNode(node.front);
|
||||
} else {
|
||||
// Камера сзади ноды
|
||||
if (_orthographic || cameraAngle > -viewAngle) {
|
||||
renderBSPNode(node.front);
|
||||
}
|
||||
renderBSPNode(node.back);
|
||||
}
|
||||
} else {
|
||||
// В ноде несколько примитивов
|
||||
if (_orthographic ? (cameraAngle < 0) : (cameraOffset > 0)) {
|
||||
// Камера спереди ноды
|
||||
if (_orthographic || cameraAngle < viewAngle) {
|
||||
renderBSPNode(node.back);
|
||||
for (primitive in node.frontPrimitives) {
|
||||
drawSkin(primitive);
|
||||
}
|
||||
}
|
||||
renderBSPNode(node.front);
|
||||
} else {
|
||||
// Камера сзади ноды
|
||||
if (_orthographic || cameraAngle > -viewAngle) {
|
||||
renderBSPNode(node.front);
|
||||
for (primitive in node.backPrimitives) {
|
||||
drawSkin(primitive);
|
||||
}
|
||||
}
|
||||
renderBSPNode(node.back);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Отрисовка скина примитива
|
||||
*/
|
||||
private function drawSkin(primitive:PolyPrimitive):void {
|
||||
if (!fullDraw && currentSkin != null && currentSkin.primitive == primitive && !_scene.changedPrimitives[primitive]) {
|
||||
// Пропуск скина
|
||||
prevSkin = currentSkin;
|
||||
currentSkin = currentSkin.nextSkin;
|
||||
} else {
|
||||
// Проверка поверхности
|
||||
var surface:Surface = primitive.face._surface;
|
||||
if (surface == null) {
|
||||
return;
|
||||
}
|
||||
// Проверка материала
|
||||
var material:SurfaceMaterial = surface._material;
|
||||
if (material == null || !material.canDraw(primitive)) {
|
||||
return;
|
||||
}
|
||||
// Отсечение выходящих за окно просмотра частей
|
||||
var i:uint;
|
||||
var length:uint = primitive.num;
|
||||
var primitivePoint:Point3D;
|
||||
var primitiveUV:Point;
|
||||
var point:DrawPoint;
|
||||
var useUV:Boolean = !_orthographic && material.useUV;
|
||||
if (useUV) {
|
||||
// Формируем список точек и UV-координат полигона
|
||||
for (i = 0; i < length; i++) {
|
||||
primitivePoint = primitive.points[i];
|
||||
primitiveUV = primitive.uvs[i];
|
||||
point = points1[i];
|
||||
if (point == null) {
|
||||
points1[i] = new DrawPoint(primitivePoint.x, primitivePoint.y, primitivePoint.z, primitiveUV.x, primitiveUV.y);
|
||||
} else {
|
||||
point.x = primitivePoint.x;
|
||||
point.y = primitivePoint.y;
|
||||
point.z = primitivePoint.z;
|
||||
point.u = primitiveUV.x;
|
||||
point.v = primitiveUV.y;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Формируем список точек полигона
|
||||
for (i = 0; i < length; i++) {
|
||||
primitivePoint = primitive.points[i];
|
||||
point = points1[i];
|
||||
if (point == null) {
|
||||
points1[i] = new DrawPoint(primitivePoint.x, primitivePoint.y, primitivePoint.z);
|
||||
} else {
|
||||
point.x = primitivePoint.x;
|
||||
point.y = primitivePoint.y;
|
||||
point.z = primitivePoint.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Отсечение по левой стороне
|
||||
length = clip(length, points1, points2, leftPlane, leftOffset, useUV);
|
||||
if (length < 3) {
|
||||
return;
|
||||
}
|
||||
// Отсечение по правой стороне
|
||||
length = clip(length, points2, points1, rightPlane, rightOffset, useUV);
|
||||
if (length < 3) {
|
||||
return;
|
||||
}
|
||||
// Отсечение по верхней стороне
|
||||
length = clip(length, points1, points2, topPlane, topOffset, useUV);
|
||||
if (length < 3) {
|
||||
return;
|
||||
}
|
||||
// Отсечение по нижней стороне
|
||||
length = clip(length, points2, points1, bottomPlane, bottomOffset, useUV);
|
||||
if (length < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fullDraw || _scene.changedPrimitives[primitive]) {
|
||||
|
||||
// Если конец списка скинов
|
||||
if (currentSkin == null) {
|
||||
// Добавляем скин в конец
|
||||
addCurrentSkin();
|
||||
} else {
|
||||
if (fullDraw || _scene.changedPrimitives[currentSkin.primitive]) {
|
||||
// Очистка скина
|
||||
currentSkin.material.clear(currentSkin);
|
||||
} else {
|
||||
// Вставка скина перед текущим
|
||||
insertCurrentSkin();
|
||||
}
|
||||
}
|
||||
|
||||
// Переводим координаты в систему камеры
|
||||
var x:Number;
|
||||
var y:Number;
|
||||
var z:Number;
|
||||
for (i = 0; i < length; i++) {
|
||||
point = points1[i];
|
||||
x = point.x;
|
||||
y = point.y;
|
||||
z = point.z;
|
||||
point.x = cameraMatrix.a*x + cameraMatrix.b*y + cameraMatrix.c*z + cameraMatrix.d;
|
||||
point.y = cameraMatrix.e*x + cameraMatrix.f*y + cameraMatrix.g*z + cameraMatrix.h;
|
||||
point.z = cameraMatrix.i*x + cameraMatrix.j*y + cameraMatrix.k*z + cameraMatrix.l;
|
||||
}
|
||||
|
||||
// Назначаем скину примитив и материал
|
||||
currentSkin.primitive = primitive;
|
||||
currentSkin.material = material;
|
||||
material.draw(this, currentSkin, length, points1);
|
||||
|
||||
// Переключаемся на следующий скин
|
||||
prevSkin = currentSkin;
|
||||
currentSkin = currentSkin.nextSkin;
|
||||
|
||||
} else {
|
||||
|
||||
// Удаление ненужных скинов
|
||||
while (currentSkin != null && _scene.changedPrimitives[currentSkin.primitive]) {
|
||||
removeCurrentSkin();
|
||||
}
|
||||
|
||||
// Переключение на следующий скин
|
||||
if (currentSkin != null) {
|
||||
prevSkin = currentSkin;
|
||||
currentSkin = currentSkin.nextSkin;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Отсечение полигона плоскостью.
|
||||
*/
|
||||
private function clip(length:uint, points1:Array, points2:Array, plane:Point3D, offset:Number, calculateUV:Boolean):uint {
|
||||
var i:uint;
|
||||
var k:Number;
|
||||
var index:uint = 0;
|
||||
var point:DrawPoint;
|
||||
var point1:DrawPoint;
|
||||
var point2:DrawPoint;
|
||||
var offset1:Number;
|
||||
var offset2:Number;
|
||||
|
||||
point1 = points1[length - 1];
|
||||
offset1 = plane.x*point1.x + plane.y*point1.y + plane.z*point1.z - offset;
|
||||
|
||||
if (calculateUV) {
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
|
||||
point2 = points1[i];
|
||||
offset2 = plane.x*point2.x + plane.y*point2.y + plane.z*point2.z - offset;
|
||||
|
||||
if (offset2 > 0) {
|
||||
if (offset1 <= 0) {
|
||||
k = offset2/(offset2 - offset1);
|
||||
point = points2[index];
|
||||
if (point == null) {
|
||||
point = new DrawPoint(point2.x - (point2.x - point1.x)*k, point2.y - (point2.y - point1.y)*k, point2.z - (point2.z - point1.z)*k, point2.u - (point2.u - point1.u)*k, point2.v - (point2.v - point1.v)*k);
|
||||
points2[index] = point;
|
||||
} else {
|
||||
point.x = point2.x - (point2.x - point1.x)*k;
|
||||
point.y = point2.y - (point2.y - point1.y)*k;
|
||||
point.z = point2.z - (point2.z - point1.z)*k;
|
||||
point.u = point2.u - (point2.u - point1.u)*k;
|
||||
point.v = point2.v - (point2.v - point1.v)*k;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
point = points2[index];
|
||||
if (point == null) {
|
||||
point = new DrawPoint(point2.x, point2.y, point2.z, point2.u, point2.v);
|
||||
points2[index] = point;
|
||||
} else {
|
||||
point.x = point2.x;
|
||||
point.y = point2.y;
|
||||
point.z = point2.z;
|
||||
point.u = point2.u;
|
||||
point.v = point2.v;
|
||||
}
|
||||
index++;
|
||||
} else {
|
||||
if (offset1 > 0) {
|
||||
k = offset2/(offset2 - offset1);
|
||||
point = points2[index];
|
||||
if (point == null) {
|
||||
point = new DrawPoint(point2.x - (point2.x - point1.x)*k, point2.y - (point2.y - point1.y)*k, point2.z - (point2.z - point1.z)*k, point2.u - (point2.u - point1.u)*k, point2.v - (point2.v - point1.v)*k);
|
||||
points2[index] = point;
|
||||
} else {
|
||||
point.x = point2.x - (point2.x - point1.x)*k;
|
||||
point.y = point2.y - (point2.y - point1.y)*k;
|
||||
point.z = point2.z - (point2.z - point1.z)*k;
|
||||
point.u = point2.u - (point2.u - point1.u)*k;
|
||||
point.v = point2.v - (point2.v - point1.v)*k;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
offset1 = offset2;
|
||||
point1 = point2;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
|
||||
point2 = points1[i];
|
||||
offset2 = plane.x*point2.x + plane.y*point2.y + plane.z*point2.z - offset;
|
||||
|
||||
if (offset2 > 0) {
|
||||
if (offset1 <= 0) {
|
||||
k = offset2/(offset2 - offset1);
|
||||
point = points2[index];
|
||||
if (point == null) {
|
||||
point = new DrawPoint(point2.x - (point2.x - point1.x)*k, point2.y - (point2.y - point1.y)*k, point2.z - (point2.z - point1.z)*k);
|
||||
points2[index] = point;
|
||||
} else {
|
||||
point.x = point2.x - (point2.x - point1.x)*k;
|
||||
point.y = point2.y - (point2.y - point1.y)*k;
|
||||
point.z = point2.z - (point2.z - point1.z)*k;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
point = points2[index];
|
||||
if (point == null) {
|
||||
point = new DrawPoint(point2.x, point2.y, point2.z);
|
||||
points2[index] = point;
|
||||
} else {
|
||||
point.x = point2.x;
|
||||
point.y = point2.y;
|
||||
point.z = point2.z;
|
||||
}
|
||||
index++;
|
||||
} else {
|
||||
if (offset1 > 0) {
|
||||
k = offset2/(offset2 - offset1);
|
||||
point = points2[index];
|
||||
if (point == null) {
|
||||
point = new DrawPoint(point2.x - (point2.x - point1.x)*k, point2.y - (point2.y - point1.y)*k, point2.z - (point2.z - point1.z)*k);
|
||||
points2[index] = point;
|
||||
} else {
|
||||
point.x = point2.x - (point2.x - point1.x)*k;
|
||||
point.y = point2.y - (point2.y - point1.y)*k;
|
||||
point.z = point2.z - (point2.z - point1.z)*k;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
offset1 = offset2;
|
||||
point1 = point2;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Добавление текущего скина.
|
||||
*/
|
||||
private function addCurrentSkin():void {
|
||||
currentSkin = Skin.createSkin();
|
||||
_view.canvas.addChild(currentSkin);
|
||||
if (prevSkin == null) {
|
||||
firstSkin = currentSkin;
|
||||
} else {
|
||||
prevSkin.nextSkin = currentSkin;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Вставляем под текущий скин.
|
||||
*/
|
||||
private function insertCurrentSkin():void {
|
||||
var skin:Skin = Skin.createSkin();
|
||||
_view.canvas.addChildAt(skin, _view.canvas.getChildIndex(currentSkin));
|
||||
skin.nextSkin = currentSkin;
|
||||
if (prevSkin == null) {
|
||||
firstSkin = skin;
|
||||
} else {
|
||||
prevSkin.nextSkin = skin;
|
||||
}
|
||||
currentSkin = skin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаляет текущий скин.
|
||||
*/
|
||||
private function removeCurrentSkin():void {
|
||||
// Сохраняем следующий
|
||||
var next:Skin = currentSkin.nextSkin;
|
||||
// Удаляем из канваса
|
||||
_view.canvas.removeChild(currentSkin);
|
||||
// Очистка скина
|
||||
if (currentSkin.material != null) {
|
||||
currentSkin.material.clear(currentSkin);
|
||||
}
|
||||
// Зачищаем ссылки
|
||||
currentSkin.nextSkin = null;
|
||||
currentSkin.primitive = null;
|
||||
currentSkin.material = null;
|
||||
// Удаляем
|
||||
Skin.destroySkin(currentSkin);
|
||||
// Следующий устанавливаем текущим
|
||||
currentSkin = next;
|
||||
// Устанавливаем связь от предыдущего скина
|
||||
if (prevSkin == null) {
|
||||
firstSkin = currentSkin;
|
||||
} else {
|
||||
prevSkin.nextSkin = currentSkin;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function calculateUVMatrix(face:Face, width:uint, height:uint):void {
|
||||
|
||||
// Расчёт точек базового примитива в координатах камеры
|
||||
var point:Point3D = face.primitive.points[0];
|
||||
textureA.x = cameraMatrix.a*point.x + cameraMatrix.b*point.y + cameraMatrix.c*point.z;
|
||||
textureA.y = cameraMatrix.e*point.x + cameraMatrix.f*point.y + cameraMatrix.g*point.z;
|
||||
point = face.primitive.points[1];
|
||||
textureB.x = cameraMatrix.a*point.x + cameraMatrix.b*point.y + cameraMatrix.c*point.z;
|
||||
textureB.y = cameraMatrix.e*point.x + cameraMatrix.f*point.y + cameraMatrix.g*point.z;
|
||||
point = face.primitive.points[2];
|
||||
textureC.x = cameraMatrix.a*point.x + cameraMatrix.b*point.y + cameraMatrix.c*point.z;
|
||||
textureC.y = cameraMatrix.e*point.x + cameraMatrix.f*point.y + cameraMatrix.g*point.z;
|
||||
|
||||
// Находим AB и AC
|
||||
var abx:Number = textureB.x - textureA.x;
|
||||
var aby:Number = textureB.y - textureA.y;
|
||||
var acx:Number = textureC.x - textureA.x;
|
||||
var acy:Number = textureC.y - textureA.y;
|
||||
|
||||
// Расчёт текстурной матрицы
|
||||
var uvMatrixBase:Matrix = face.uvMatrixBase;
|
||||
var uvMatrix:Matrix = face.uvMatrix;
|
||||
uvMatrix.a = (uvMatrixBase.a*abx + uvMatrixBase.b*acx)/width;
|
||||
uvMatrix.b = (uvMatrixBase.a*aby + uvMatrixBase.b*acy)/width;
|
||||
uvMatrix.c = -(uvMatrixBase.c*abx + uvMatrixBase.d*acx)/height;
|
||||
uvMatrix.d = -(uvMatrixBase.c*aby + uvMatrixBase.d*acy)/height;
|
||||
uvMatrix.tx = (uvMatrixBase.tx + uvMatrixBase.c)*abx + (uvMatrixBase.ty + uvMatrixBase.d)*acx + textureA.x + cameraMatrix.d;
|
||||
uvMatrix.ty = (uvMatrixBase.tx + uvMatrixBase.c)*aby + (uvMatrixBase.ty + uvMatrixBase.d)*acy + textureA.y + cameraMatrix.h;
|
||||
|
||||
// Помечаем, как рассчитанную
|
||||
uvMatricesCalculated[face] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вид, в который происходит отрисовка камеры.
|
||||
*/
|
||||
public function get view():View {
|
||||
return _view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set view(value:View):void {
|
||||
if (value != _view) {
|
||||
if (_view != null) {
|
||||
_view.camera = null;
|
||||
}
|
||||
if (value != null) {
|
||||
value.camera = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Включение режима прямоугольной (изометрической) проекции.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
public function get orthographic():Boolean {
|
||||
return _orthographic;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set orthographic(value:Boolean):void {
|
||||
if (_orthographic != value) {
|
||||
// Отправляем сигнал об изменении типа камеры
|
||||
addOperationToScene(calculateMatrixOperation);
|
||||
// Сохраняем новое значение
|
||||
_orthographic = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Угол поля зрения в радианах в режиме перспективной проекции.
|
||||
*/
|
||||
public function get fov():Number {
|
||||
return _fov;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set fov(value:Number):void {
|
||||
value = (value < 0) ? 0 : ((value > (Math.PI - 0.0001)) ? (Math.PI - 0.0001) : value);
|
||||
if (_fov != value) {
|
||||
// Если перспектива
|
||||
if (!_orthographic) {
|
||||
// Отправляем сигнал об изменении плоскостей отсечения
|
||||
addOperationToScene(calculatePlanesOperation);
|
||||
}
|
||||
// Сохраняем новое значение
|
||||
_fov = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Коэффициент увеличения изображения в режиме изометрической проекции.
|
||||
*/
|
||||
public function get zoom():Number {
|
||||
return _zoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set zoom(value:Number):void {
|
||||
value = (value < 0) ? 0 : value;
|
||||
if (_zoom != value) {
|
||||
// Если изометрия
|
||||
if (_orthographic) {
|
||||
// Отправляем сигнал об изменении zoom
|
||||
addOperationToScene(calculateMatrixOperation);
|
||||
}
|
||||
// Сохраняем новое значение
|
||||
_zoom = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override protected function addToScene(scene:Scene3D):void {
|
||||
super.addToScene(scene);
|
||||
if (_view != null) {
|
||||
// Отправляем операцию расчёта плоскостей отсечения
|
||||
scene.addOperation(calculatePlanesOperation);
|
||||
// Подписываемся на сигналы сцены
|
||||
scene.changePrimitivesOperation.addSequel(renderOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override protected function removeFromScene(scene:Scene3D):void {
|
||||
super.removeFromScene(scene);
|
||||
|
||||
// Удаляем все операции из очереди
|
||||
scene.removeOperation(calculateMatrixOperation);
|
||||
scene.removeOperation(calculatePlanesOperation);
|
||||
scene.removeOperation(renderOperation);
|
||||
|
||||
if (_view != null) {
|
||||
// Отписываемся от сигналов сцены
|
||||
scene.changePrimitivesOperation.removeSequel(renderOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function addToView(view:View):void {
|
||||
// Сохраняем первый скин
|
||||
firstSkin = (view.canvas.numChildren > 0) ? Skin(view.canvas.getChildAt(0)) : null;
|
||||
|
||||
// Подписка на свои операции
|
||||
|
||||
// При изменении камеры пересчёт матрицы
|
||||
calculateTransformationOperation.addSequel(calculateMatrixOperation);
|
||||
// При изменении матрицы или FOV пересчёт плоскостей отсечения
|
||||
calculateMatrixOperation.addSequel(calculatePlanesOperation);
|
||||
// При изменении плоскостей перерисовка
|
||||
calculatePlanesOperation.addSequel(renderOperation);
|
||||
|
||||
if (_scene != null) {
|
||||
// Отправляем сигнал перерисовки
|
||||
_scene.addOperation(calculateMatrixOperation);
|
||||
// Подписываемся на сигналы сцены
|
||||
_scene.changePrimitivesOperation.addSequel(renderOperation);
|
||||
}
|
||||
|
||||
// Сохраняем вид
|
||||
_view = view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function removeFromView(view:View):void {
|
||||
// Сброс ссылки на первый скин
|
||||
firstSkin = null;
|
||||
|
||||
// Отписка от своих операций
|
||||
|
||||
// При изменении камеры пересчёт матрицы
|
||||
calculateTransformationOperation.removeSequel(calculateMatrixOperation);
|
||||
// При изменении матрицы или FOV пересчёт плоскостей отсечения
|
||||
calculateMatrixOperation.removeSequel(calculatePlanesOperation);
|
||||
// При изменении плоскостей перерисовка
|
||||
calculatePlanesOperation.removeSequel(renderOperation);
|
||||
|
||||
if (_scene != null) {
|
||||
// Удаляем все операции из очереди
|
||||
_scene.removeOperation(calculateMatrixOperation);
|
||||
_scene.removeOperation(calculatePlanesOperation);
|
||||
_scene.removeOperation(renderOperation);
|
||||
// Отписываемся от сигналов сцены
|
||||
_scene.changePrimitivesOperation.removeSequel(renderOperation);
|
||||
}
|
||||
// Удаляем ссылку на вид
|
||||
_view = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override protected function defaultName():String {
|
||||
return "camera" + ++counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected override function createEmptyObject():Object3D {
|
||||
return new Camera3D();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected override function clonePropertiesFrom(source:Object3D):void {
|
||||
super.clonePropertiesFrom(source);
|
||||
|
||||
var src:Camera3D = Camera3D(source);
|
||||
orthographic = src._orthographic;
|
||||
zoom = src._zoom;
|
||||
fov = src._fov;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,907 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.types.Point3D;
|
||||
import alternativa.types.Set;
|
||||
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Point;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Грань, образованная тремя или более вершинами.
|
||||
*/
|
||||
final public class Face {
|
||||
// Операции
|
||||
/**
|
||||
* @private
|
||||
* Расчёт глобальной нормали плоскости грани.
|
||||
*/
|
||||
alternativa3d var calculateNormalOperation:Operation = new Operation("calculateNormal", this, calculateNormal, Operation.FACE_CALCULATE_NORMAL);
|
||||
/**
|
||||
* @private
|
||||
* Расчёт UV-координат (выполняется до трансформации, чтобы UV корректно разбились при построении BSP).
|
||||
*/
|
||||
alternativa3d var calculateUVOperation:Operation = new Operation("calculateUV", this, calculateUV, Operation.FACE_CALCULATE_UV);
|
||||
/**
|
||||
* @private
|
||||
* Обновление примитива в сцене.
|
||||
*/
|
||||
alternativa3d var updatePrimitiveOperation:Operation = new Operation("updatePrimitive", this, updatePrimitive, Operation.FACE_UPDATE_PRIMITIVE);
|
||||
/**
|
||||
* @private
|
||||
* Обновление материала.
|
||||
*/
|
||||
alternativa3d var updateMaterialOperation:Operation = new Operation("updateMaterial", this, updateMaterial, Operation.FACE_UPDATE_MATERIAL);
|
||||
/**
|
||||
* @private
|
||||
* Расчёт UV для фрагментов (выполняется после трансформации, если её не было).
|
||||
*/
|
||||
alternativa3d var calculateFragmentsUVOperation:Operation = new Operation("calculateFragmentsUV", this, calculateFragmentsUV, Operation.FACE_CALCULATE_FRAGMENTS_UV);
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Меш
|
||||
*/
|
||||
alternativa3d var _mesh:Mesh;
|
||||
/**
|
||||
* @private
|
||||
* Поверхность
|
||||
*/
|
||||
alternativa3d var _surface:Surface;
|
||||
/**
|
||||
* @private
|
||||
* Вершины грани
|
||||
*/
|
||||
alternativa3d var _vertices:Array;
|
||||
/**
|
||||
* @private
|
||||
* Количество вершин
|
||||
*/
|
||||
alternativa3d var _num:uint;
|
||||
/**
|
||||
* @private
|
||||
* Примитив
|
||||
*/
|
||||
alternativa3d var primitive:PolyPrimitive;
|
||||
|
||||
// UV-координаты
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _aUV:Point;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _bUV:Point;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _cUV:Point;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Коэффициенты базовой UV-матрицы
|
||||
*/
|
||||
alternativa3d var uvMatrixBase:Matrix;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* UV Матрица перевода текстурных координат в изометрическую камеру.
|
||||
*/
|
||||
alternativa3d var uvMatrix:Matrix;
|
||||
/**
|
||||
* @private
|
||||
* Нормаль плоскости
|
||||
*/
|
||||
alternativa3d var globalNormal:Point3D = new Point3D();
|
||||
/**
|
||||
* @private
|
||||
* Смещение плоскости
|
||||
*/
|
||||
alternativa3d var globalOffset:Number;
|
||||
|
||||
/**
|
||||
* Создание экземпляра грани.
|
||||
*
|
||||
* @param vertices массив объектов типа <code>alternativa.engine3d.core.Vertex</code>, задающий вершины грани в
|
||||
* порядке обхода лицевой стороны грани против часовой стрелки.
|
||||
*
|
||||
* @see Vertex
|
||||
*/
|
||||
public function Face(vertices:Array) {
|
||||
// Сохраняем вершины
|
||||
_vertices = vertices;
|
||||
_num = vertices.length;
|
||||
|
||||
// Создаём оригинальный примитив
|
||||
primitive = PolyPrimitive.createPolyPrimitive();
|
||||
primitive.face = this;
|
||||
primitive.num = _num;
|
||||
|
||||
// Обрабатываем вершины
|
||||
for (var i:uint = 0; i < _num; i++) {
|
||||
var vertex:Vertex = vertices[i];
|
||||
// Добавляем координаты вершины в примитив
|
||||
primitive.points.push(vertex.globalCoords);
|
||||
// Добавляем пустые UV-координаты в примитив
|
||||
primitive.uvs.push(null);
|
||||
// Добавляем вершину в грань
|
||||
vertex.addToFace(this);
|
||||
}
|
||||
|
||||
// Расчёт нормали
|
||||
calculateNormalOperation.addSequel(updatePrimitiveOperation);
|
||||
|
||||
// Расчёт UV грани инициирует расчёт UV фрагментов
|
||||
calculateUVOperation.addSequel(calculateFragmentsUVOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчёт нормали в глобальных координатах
|
||||
*/
|
||||
private function calculateNormal():void {
|
||||
// Вектор AB
|
||||
var vertex:Vertex = _vertices[0];
|
||||
var av:Point3D = vertex.globalCoords;
|
||||
vertex = _vertices[1];
|
||||
var bv:Point3D = vertex.globalCoords;
|
||||
var abx:Number = bv.x - av.x;
|
||||
var aby:Number = bv.y - av.y;
|
||||
var abz:Number = bv.z - av.z;
|
||||
// Вектор AC
|
||||
vertex = _vertices[2];
|
||||
var cv:Point3D = vertex.globalCoords;
|
||||
var acx:Number = cv.x - av.x;
|
||||
var acy:Number = cv.y - av.y;
|
||||
var acz:Number = cv.z - av.z;
|
||||
// Перпендикуляр к плоскости
|
||||
globalNormal.x = acz*aby - acy*abz;
|
||||
globalNormal.y = acx*abz - acz*abx;
|
||||
globalNormal.z = acy*abx - acx*aby;
|
||||
// Нормализация перпендикуляра
|
||||
globalNormal.normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчитывает глобальное смещение плоскости грани.
|
||||
* Помечает конечные примитивы на удаление, а базовый на добавление в сцене.
|
||||
*/
|
||||
private function updatePrimitive():void {
|
||||
// Расчёт смещения
|
||||
var vertex:Vertex = _vertices[0];
|
||||
globalOffset = vertex.globalCoords.x*globalNormal.x + vertex.globalCoords.y*globalNormal.y + vertex.globalCoords.z*globalNormal.z;
|
||||
|
||||
removePrimitive(primitive);
|
||||
primitive.mobility = _mesh.inheritedMobility;
|
||||
_mesh._scene.addPrimitives.push(primitive);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Рекурсивно проходит по фрагментам примитива и отправляет конечные фрагменты на удаление из сцены
|
||||
*/
|
||||
private function removePrimitive(primitive:PolyPrimitive):void {
|
||||
if (primitive.fragment1 != null) {
|
||||
removePrimitive(primitive.fragment1);
|
||||
removePrimitive(primitive.fragment2);
|
||||
primitive.fragment1 = null;
|
||||
primitive.fragment2 = null;
|
||||
if (primitive != this.primitive) {
|
||||
primitive.parent = null;
|
||||
primitive.sibling = null;
|
||||
PolyPrimitive.destroyPolyPrimitive(primitive);
|
||||
}
|
||||
} else {
|
||||
// Если примитив в BSP-дереве
|
||||
if (primitive.node != null) {
|
||||
// Удаление примитива
|
||||
_mesh._scene.removeBSPPrimitive(primitive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Пометка на перерисовку фрагментов грани.
|
||||
*/
|
||||
private function updateMaterial():void {
|
||||
if (!updatePrimitiveOperation.queued) {
|
||||
changePrimitive(primitive);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Рекурсивно проходит по фрагментам примитива и отправляет конечные фрагменты на перерисовку
|
||||
*/
|
||||
private function changePrimitive(primitive:PolyPrimitive):void {
|
||||
if (primitive.fragment1 != null) {
|
||||
changePrimitive(primitive.fragment1);
|
||||
changePrimitive(primitive.fragment2);
|
||||
} else {
|
||||
_mesh._scene.changedPrimitives[primitive] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчёт UV-матрицы на основании первых трёх UV-координат.
|
||||
* Расчёт UV-координат для оставшихся точек.
|
||||
*/
|
||||
private function calculateUV():void {
|
||||
var i:uint;
|
||||
// Расчёт UV-матрицы
|
||||
if (_aUV != null && _bUV != null && _cUV != null) {
|
||||
var abu:Number = _bUV.x - _aUV.x;
|
||||
var abv:Number = _bUV.y - _aUV.y;
|
||||
var acu:Number = _cUV.x - _aUV.x;
|
||||
var acv:Number = _cUV.y - _aUV.y;
|
||||
var det:Number = abu*acv - abv*acu;
|
||||
if (uvMatrixBase == null) {
|
||||
uvMatrixBase = new Matrix();
|
||||
uvMatrix = new Matrix();
|
||||
}
|
||||
uvMatrixBase.a = acv/det;
|
||||
uvMatrixBase.b = -abv/det;
|
||||
uvMatrixBase.c = -acu/det;
|
||||
uvMatrixBase.d = abu/det;
|
||||
uvMatrixBase.tx = -(uvMatrixBase.a*_aUV.x + uvMatrixBase.c*_aUV.y);
|
||||
uvMatrixBase.ty = -(uvMatrixBase.b*_aUV.x + uvMatrixBase.d*_aUV.y);
|
||||
|
||||
// Заполняем UV в базовом примитиве
|
||||
primitive.uvs[0] = _aUV;
|
||||
primitive.uvs[1] = _bUV;
|
||||
primitive.uvs[2] = _cUV;
|
||||
|
||||
// Расчёт недостающих UV
|
||||
if (_num > 3) {
|
||||
var a:Point3D = primitive.points[0];
|
||||
var b:Point3D = primitive.points[1];
|
||||
var c:Point3D = primitive.points[2];
|
||||
|
||||
var ab1:Number;
|
||||
var ab2:Number;
|
||||
var ac1:Number;
|
||||
var ac2:Number;
|
||||
var ad1:Number;
|
||||
var ad2:Number;
|
||||
var abk:Number;
|
||||
var ack:Number;
|
||||
|
||||
var uv:Point;
|
||||
var point:Point3D;
|
||||
|
||||
// Выбор наиболее подходящих осей для расчёта
|
||||
if (((globalNormal.x < 0) ? -globalNormal.x : globalNormal.x) > ((globalNormal.y < 0) ? -globalNormal.y : globalNormal.y)) {
|
||||
if (((globalNormal.x < 0) ? -globalNormal.x : globalNormal.x) > ((globalNormal.z < 0) ? -globalNormal.z : globalNormal.z)) {
|
||||
// Ось X
|
||||
ab1 = b.y - a.y;
|
||||
ab2 = b.z - a.z;
|
||||
ac1 = c.y - a.y;
|
||||
ac2 = c.z - a.z;
|
||||
det = ab1*ac2 - ac1*ab2;
|
||||
for (i = 3; i < _num; i++) {
|
||||
point = primitive.points[i];
|
||||
ad1 = point.y - a.y;
|
||||
ad2 = point.z - a.z;
|
||||
abk = (ad1*ac2 - ac1*ad2)/det;
|
||||
ack = (ab1*ad2 - ad1*ab2)/det;
|
||||
uv = primitive.uvs[i];
|
||||
if (uv == null) {
|
||||
uv = new Point();
|
||||
primitive.uvs[i] = uv;
|
||||
}
|
||||
uv.x = _aUV.x + abu*abk + acu*ack;
|
||||
uv.y = _aUV.y + abv*abk + acv*ack;
|
||||
}
|
||||
} else {
|
||||
// Ось Z
|
||||
ab1 = b.x - a.x;
|
||||
ab2 = b.y - a.y;
|
||||
ac1 = c.x - a.x;
|
||||
ac2 = c.y - a.y;
|
||||
det = ab1*ac2 - ac1*ab2;
|
||||
for (i = 3; i < _num; i++) {
|
||||
point = primitive.points[i];
|
||||
ad1 = point.x - a.x;
|
||||
ad2 = point.y - a.y;
|
||||
abk = (ad1*ac2 - ac1*ad2)/det;
|
||||
ack = (ab1*ad2 - ad1*ab2)/det;
|
||||
uv = primitive.uvs[i];
|
||||
if (uv == null) {
|
||||
uv = new Point();
|
||||
primitive.uvs[i] = uv;
|
||||
}
|
||||
uv.x = _aUV.x + abu*abk + acu*ack;
|
||||
uv.y = _aUV.y + abv*abk + acv*ack;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (((globalNormal.y < 0) ? -globalNormal.y : globalNormal.y) > ((globalNormal.z < 0) ? -globalNormal.z : globalNormal.z)) {
|
||||
// Ось Y
|
||||
ab1 = b.x - a.x;
|
||||
ab2 = b.z - a.z;
|
||||
ac1 = c.x - a.x;
|
||||
ac2 = c.z - a.z;
|
||||
det = ab1*ac2 - ac1*ab2;
|
||||
for (i = 3; i < _num; i++) {
|
||||
point = primitive.points[i];
|
||||
ad1 = point.x - a.x;
|
||||
ad2 = point.z - a.z;
|
||||
abk = (ad1*ac2 - ac1*ad2)/det;
|
||||
ack = (ab1*ad2 - ad1*ab2)/det;
|
||||
uv = primitive.uvs[i];
|
||||
if (uv == null) {
|
||||
uv = new Point();
|
||||
primitive.uvs[i] = uv;
|
||||
}
|
||||
uv.x = _aUV.x + abu*abk + acu*ack;
|
||||
uv.y = _aUV.y + abv*abk + acv*ack;
|
||||
}
|
||||
} else {
|
||||
// Ось Z
|
||||
ab1 = b.x - a.x;
|
||||
ab2 = b.y - a.y;
|
||||
ac1 = c.x - a.x;
|
||||
ac2 = c.y - a.y;
|
||||
det = ab1*ac2 - ac1*ab2;
|
||||
for (i = 3; i < _num; i++) {
|
||||
point = primitive.points[i];
|
||||
ad1 = point.x - a.x;
|
||||
ad2 = point.y - a.y;
|
||||
abk = (ad1*ac2 - ac1*ad2)/det;
|
||||
ack = (ab1*ad2 - ad1*ab2)/det;
|
||||
uv = primitive.uvs[i];
|
||||
if (uv == null) {
|
||||
uv = new Point();
|
||||
primitive.uvs[i] = uv;
|
||||
}
|
||||
uv.x = _aUV.x + abu*abk + acu*ack;
|
||||
uv.y = _aUV.y + abv*abk + acv*ack;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Удаляем UV-матрицу
|
||||
uvMatrixBase = null;
|
||||
uvMatrix = null;
|
||||
// Удаляем UV-координаты из базового примитива
|
||||
for (i = 0; i < _num; i++) {
|
||||
primitive.uvs[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчёт UV-координат для фрагментов примитива, если не было трансформации
|
||||
*/
|
||||
private function calculateFragmentsUV():void {
|
||||
// Если в этом цикле не было трансформации
|
||||
if (!updatePrimitiveOperation.queued) {
|
||||
if (uvMatrixBase != null) {
|
||||
// Рассчитываем UV в примитиве
|
||||
calculatePrimitiveUV(primitive);
|
||||
} else {
|
||||
// Удаляем UV в примитиве
|
||||
removePrimitiveUV(primitive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчёт UV для точек базового примитива.
|
||||
*
|
||||
* @param primitive
|
||||
*/
|
||||
private function calculatePrimitiveUV(primitive:PolyPrimitive):void {
|
||||
if (primitive.fragment1 != null) {
|
||||
var points:Array = primitive.points;
|
||||
var points1:Array = primitive.fragment1.points;
|
||||
var points2:Array = primitive.fragment2.points;
|
||||
var uvs:Array = primitive.uvs;
|
||||
var uvs1:Array = primitive.fragment1.uvs;
|
||||
var uvs2:Array = primitive.fragment2.uvs;
|
||||
var index1:uint = 0;
|
||||
var index2:uint = 0;
|
||||
var point:Point3D;
|
||||
var uv:Point;
|
||||
var uv1:Point;
|
||||
var uv2:Point;
|
||||
var t:Number;
|
||||
var firstSplit:Boolean = true;
|
||||
for (var i:uint = 0; i < primitive.num; i++) {
|
||||
var split:Boolean = true;
|
||||
point = points[i];
|
||||
if (point == points2[index2]) {
|
||||
if (uvs2[index2] == null) {
|
||||
uvs2[index2] = uvs[i];
|
||||
}
|
||||
split = false;
|
||||
index2++;
|
||||
}
|
||||
if (point == points1[index1]) {
|
||||
if (uvs1[index1] == null) {
|
||||
uvs1[index1] = uvs[i];
|
||||
}
|
||||
split = false;
|
||||
index1++;
|
||||
}
|
||||
|
||||
if (split) {
|
||||
uv1 = uvs[(i == 0) ? (primitive.num - 1) : (i - 1)];
|
||||
uv2 = uvs[i];
|
||||
t = (firstSplit) ? primitive.splitTime1 : primitive.splitTime2;
|
||||
uv = uvs2[index2];
|
||||
if (uv == null) {
|
||||
uv = new Point(uv1.x + (uv2.x - uv1.x)*t, uv1.y + (uv2.y - uv1.y)*t);
|
||||
uvs2[index2] = uv;
|
||||
uvs1[index1] = uv;
|
||||
} else {
|
||||
uv.x = uv1.x + (uv2.x - uv1.x)*t;
|
||||
uv.y = uv1.y + (uv2.y - uv1.y)*t;
|
||||
}
|
||||
firstSplit = false;
|
||||
index2++;
|
||||
index1++;
|
||||
if (point == points2[index2]) {
|
||||
if (uvs2[index2] == null) {
|
||||
uvs2[index2] = uvs[i];
|
||||
}
|
||||
index2++;
|
||||
}
|
||||
if (point == points1[index1]) {
|
||||
if (uvs1[index1] == null) {
|
||||
uvs1[index1] = uvs[i];
|
||||
}
|
||||
index1++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Проверяем рассечение последнего ребра
|
||||
if (index2 < primitive.fragment2.num) {
|
||||
uv1 = uvs[primitive.num - 1];
|
||||
uv2 = uvs[0];
|
||||
t = (firstSplit) ? primitive.splitTime1 : primitive.splitTime2;
|
||||
uv = uvs2[index2];
|
||||
if (uv == null) {
|
||||
uv = new Point(uv1.x + (uv2.x - uv1.x)*t, uv1.y + (uv2.y - uv1.y)*t);
|
||||
uvs2[index2] = uv;
|
||||
uvs1[index1] = uv;
|
||||
} else {
|
||||
uv.x = uv1.x + (uv2.x - uv1.x)*t;
|
||||
uv.y = uv1.y + (uv2.y - uv1.y)*t;
|
||||
}
|
||||
}
|
||||
|
||||
calculatePrimitiveUV(primitive.fragment1);
|
||||
calculatePrimitiveUV(primitive.fragment2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление UV в примитиве и его фрагментах
|
||||
* @param primitive
|
||||
*/
|
||||
private function removePrimitiveUV(primitive:PolyPrimitive):void {
|
||||
// Очищаем список UV
|
||||
for (var i:uint = 0; i < primitive.num; i++) {
|
||||
primitive.uvs[i] = null;
|
||||
}
|
||||
// Если есть фрагменты, удаляем UV в них
|
||||
if (primitive.fragment1 != null) {
|
||||
removePrimitiveUV(primitive.fragment1);
|
||||
removePrimitiveUV(primitive.fragment2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Массив вершин грани, представленных объектами класса <code>alternativa.engine3d.core.Vertex</code>.
|
||||
*
|
||||
* @see Vertex
|
||||
*/
|
||||
public function get vertices():Array {
|
||||
return new Array().concat(_vertices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Количество вершин грани.
|
||||
*/
|
||||
public function get num():uint {
|
||||
return _num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mesh-объект, в котором находится грань.
|
||||
*/
|
||||
public function get mesh():Mesh {
|
||||
return _mesh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Поверхность, в которой находится грань.
|
||||
*/
|
||||
public function get surface():Surface {
|
||||
return _surface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Идентификатор грани в Mesh-объекте. В случае, если грань не содержится ни в одном Mesh-объекте, возвращается
|
||||
* <code>null</code>.
|
||||
*/
|
||||
public function get id():Object {
|
||||
return (_mesh != null) ? _mesh.getFaceId(this) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* UV-координаты, соответствующие первой вершине грани.
|
||||
*/
|
||||
public function get aUV():Point {
|
||||
return (_aUV != null) ? _aUV.clone() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* UV-координаты, соответствующие второй вершине грани.
|
||||
*/
|
||||
public function get bUV():Point {
|
||||
return (_bUV != null) ? _bUV.clone() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* UV-координаты, соответствующие третьей вершине грани.
|
||||
*/
|
||||
public function get cUV():Point {
|
||||
return (_cUV != null) ? _cUV.clone() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set aUV(value:Point):void {
|
||||
if (_aUV != null) {
|
||||
if (value != null) {
|
||||
if (!_aUV.equals(value)) {
|
||||
_aUV.x = value.x;
|
||||
_aUV.y = value.y;
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(calculateUVOperation);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_aUV = null;
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(calculateUVOperation);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (value != null) {
|
||||
_aUV = value.clone();
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(calculateUVOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set bUV(value:Point):void {
|
||||
if (_bUV != null) {
|
||||
if (value != null) {
|
||||
if (!_bUV.equals(value)) {
|
||||
_bUV.x = value.x;
|
||||
_bUV.y = value.y;
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(calculateUVOperation);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_bUV = null;
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(calculateUVOperation);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (value != null) {
|
||||
_bUV = value.clone();
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(calculateUVOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set cUV(value:Point):void {
|
||||
if (_cUV != null) {
|
||||
if (value != null) {
|
||||
if (!_cUV.equals(value)) {
|
||||
_cUV.x = value.x;
|
||||
_cUV.y = value.y;
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(calculateUVOperation);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_cUV = null;
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(calculateUVOperation);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (value != null) {
|
||||
_cUV = value.clone();
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(calculateUVOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Нормаль в локальной системе координат.
|
||||
*/
|
||||
public function get normal():Point3D {
|
||||
var res:Point3D = new Point3D();
|
||||
var vertex:Vertex = _vertices[0];
|
||||
var av:Point3D = vertex.coords;
|
||||
vertex = _vertices[1];
|
||||
var bv:Point3D = vertex.coords;
|
||||
var abx:Number = bv.x - av.x;
|
||||
var aby:Number = bv.y - av.y;
|
||||
var abz:Number = bv.z - av.z;
|
||||
vertex = _vertices[2];
|
||||
var cv:Point3D = vertex.coords;
|
||||
var acx:Number = cv.x - av.x;
|
||||
var acy:Number = cv.y - av.y;
|
||||
var acz:Number = cv.z - av.z;
|
||||
res.x = acz*aby - acy*abz;
|
||||
res.y = acx*abz - acz*abx;
|
||||
res.z = acy*abx - acx*aby;
|
||||
if (res.x != 0 || res.y != 0 || res.z != 0) {
|
||||
var k:Number = Math.sqrt(res.x*res.x + res.y*res.y + res.z*res.z);
|
||||
res.x /= k;
|
||||
res.y /= k;
|
||||
res.z /= k;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает UV-координаты для произвольной точки в системе координат полигональной сетки, которой принадлежит грань.
|
||||
*
|
||||
* @param point точка в плоскости грани, для которой производится расчёт UV-координат
|
||||
* @return UV-координаты заданной точки
|
||||
*/
|
||||
public function getUV(point:Point3D):Point {
|
||||
return getUVFast(point, normal);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчёт UV-координат для произвольной точки в локальной системе координат без расчёта
|
||||
* локальной нормали грани. Используется для оптимизации.
|
||||
*
|
||||
* @param point точка в плоскости грани, для которой производится расчёт UV-координат
|
||||
* @param normal нормаль плоскости грани в локальной системе координат
|
||||
* @return UV-координаты заданной точки
|
||||
*/
|
||||
alternativa3d function getUVFast(point:Point3D, normal:Point3D):Point {
|
||||
if (_aUV == null || _bUV == null || _cUV == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Выбор наиболее длинной оси нормали
|
||||
var dir:uint;
|
||||
if (((normal.x < 0) ? -normal.x : normal.x) > ((normal.y < 0) ? -normal.y : normal.y)) {
|
||||
if (((normal.x < 0) ? -normal.x : normal.x) > ((normal.z < 0) ? -normal.z : normal.z)) {
|
||||
dir = 0;
|
||||
} else {
|
||||
dir = 2;
|
||||
}
|
||||
} else {
|
||||
if (((normal.y < 0) ? -normal.y : normal.y) > ((normal.z < 0) ? -normal.z : normal.z)) {
|
||||
dir = 1;
|
||||
} else {
|
||||
dir = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Расчёт соотношения по векторам AB и AC
|
||||
var v:Vertex = _vertices[0];
|
||||
var a:Point3D = v._coords;
|
||||
v = _vertices[1];
|
||||
var b:Point3D = v._coords;
|
||||
v = _vertices[2];
|
||||
var c:Point3D = v._coords;
|
||||
|
||||
var ab1:Number = (dir == 0) ? (b.y - a.y) : (b.x - a.x);
|
||||
var ab2:Number = (dir == 2) ? (b.y - a.y) : (b.z - a.z);
|
||||
var ac1:Number = (dir == 0) ? (c.y - a.y) : (c.x - a.x);
|
||||
var ac2:Number = (dir == 2) ? (c.y - a.y) : (c.z - a.z);
|
||||
var det:Number = ab1*ac2 - ac1*ab2;
|
||||
|
||||
var ad1:Number = (dir == 0) ? (point.y - a.y) : (point.x - a.x);
|
||||
var ad2:Number = (dir == 2) ? (point.y - a.y) : (point.z - a.z);
|
||||
var abk:Number = (ad1*ac2 - ac1*ad2)/det;
|
||||
var ack:Number = (ab1*ad2 - ad1*ab2)/det;
|
||||
|
||||
// Интерполяция по UV первых точек
|
||||
var abu:Number = _bUV.x - _aUV.x;
|
||||
var abv:Number = _bUV.y - _aUV.y;
|
||||
var acu:Number = _cUV.x - _aUV.x;
|
||||
var acv:Number = _cUV.y - _aUV.y;
|
||||
|
||||
return new Point(_aUV.x + abu*abk + acu*ack, _aUV.y + abv*abk + acv*ack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Множество граней, имеющих общие рёбра с текущей гранью.
|
||||
*/
|
||||
public function get edgeJoinedFaces():Set {
|
||||
var res:Set = new Set(true);
|
||||
// Перебираем точки грани
|
||||
for (var i:uint = 0; i < _num; i++) {
|
||||
var a:Vertex = _vertices[i];
|
||||
var b:Vertex = _vertices[(i < _num - 1) ? (i + 1) : 0];
|
||||
|
||||
// Перебираем грани текущей точки
|
||||
for (var key:* in a._faces) {
|
||||
var face:Face = key;
|
||||
// Если это другая грань и у неё также есть следующая точка
|
||||
if (face != this && face._vertices.indexOf(b) >= 0) {
|
||||
// Значит у граней общее ребро
|
||||
res[face] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление всех вершин из грани.
|
||||
* Очистка базового примитива.
|
||||
*/
|
||||
alternativa3d function removeVertices():void {
|
||||
// Удалить вершины
|
||||
for (var i:uint = 0; i < _num; i++) {
|
||||
// Удаляем из списка
|
||||
var vertex:Vertex = _vertices.pop();
|
||||
// Удаляем координаты вершины из примитива
|
||||
primitive.points.pop();
|
||||
// Удаляем вершину из грани
|
||||
vertex.removeFromFace(this);
|
||||
}
|
||||
// Обнуляем количество вершин
|
||||
_num = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Добавление грани на сцену
|
||||
* @param scene
|
||||
*/
|
||||
alternativa3d function addToScene(scene:Scene3D):void {
|
||||
// При добавлении на сцену рассчитываем плоскость и UV
|
||||
scene.addOperation(calculateNormalOperation);
|
||||
scene.addOperation(calculateUVOperation);
|
||||
|
||||
// Подписываем сцену на операции
|
||||
updatePrimitiveOperation.addSequel(scene.calculateBSPOperation);
|
||||
updateMaterialOperation.addSequel(scene.changePrimitivesOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление грани из сцены
|
||||
* @param scene
|
||||
*/
|
||||
alternativa3d function removeFromScene(scene:Scene3D):void {
|
||||
// Удаляем все операции из очереди
|
||||
scene.removeOperation(calculateUVOperation);
|
||||
scene.removeOperation(calculateFragmentsUVOperation);
|
||||
scene.removeOperation(calculateNormalOperation);
|
||||
scene.removeOperation(updatePrimitiveOperation);
|
||||
scene.removeOperation(updateMaterialOperation);
|
||||
|
||||
// Удаляем примитивы из сцены
|
||||
removePrimitive(primitive);
|
||||
|
||||
// Посылаем операцию сцены на расчёт BSP
|
||||
scene.addOperation(scene.calculateBSPOperation);
|
||||
|
||||
// Отписываем сцену от операций
|
||||
updatePrimitiveOperation.removeSequel(scene.calculateBSPOperation);
|
||||
updateMaterialOperation.removeSequel(scene.changePrimitivesOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Добавление грани в меш
|
||||
* @param mesh
|
||||
*/
|
||||
alternativa3d function addToMesh(mesh:Mesh):void {
|
||||
// Подписка на операции меша
|
||||
mesh.changeCoordsOperation.addSequel(updatePrimitiveOperation);
|
||||
mesh.changeRotationOrScaleOperation.addSequel(calculateNormalOperation);
|
||||
mesh.calculateMobilityOperation.addSequel(updatePrimitiveOperation);
|
||||
// Сохранить меш
|
||||
_mesh = mesh;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление грани из меша
|
||||
* @param mesh
|
||||
*/
|
||||
alternativa3d function removeFromMesh(mesh:Mesh):void {
|
||||
// Отписка от операций меша
|
||||
mesh.changeCoordsOperation.removeSequel(updatePrimitiveOperation);
|
||||
mesh.changeRotationOrScaleOperation.removeSequel(calculateNormalOperation);
|
||||
mesh.calculateMobilityOperation.removeSequel(updatePrimitiveOperation);
|
||||
// Удалить ссылку на меш
|
||||
_mesh = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Добавление к поверхности
|
||||
*
|
||||
* @param surface
|
||||
*/
|
||||
alternativa3d function addToSurface(surface:Surface):void {
|
||||
// Подписка поверхности на операции
|
||||
surface.changeMaterialOperation.addSequel(updateMaterialOperation);
|
||||
// Если при смене поверхности изменился материал
|
||||
if (_mesh != null && (_surface != null && _surface._material != surface._material || _surface == null && surface._material != null)) {
|
||||
// Отправляем сигнал смены материала
|
||||
_mesh.addOperationToScene(updateMaterialOperation);
|
||||
}
|
||||
// Сохранить поверхность
|
||||
_surface = surface;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление из поверхности
|
||||
*
|
||||
* @param surface
|
||||
*/
|
||||
alternativa3d function removeFromSurface(surface:Surface):void {
|
||||
// Отписка поверхности от операций
|
||||
surface.changeMaterialOperation.removeSequel(updateMaterialOperation);
|
||||
// Если был материал
|
||||
if (surface._material != null) {
|
||||
// Отправляем сигнал смены материала
|
||||
_mesh.addOperationToScene(updateMaterialOperation);
|
||||
}
|
||||
// Удалить ссылку на поверхность
|
||||
_surface = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Строковое представление объекта.
|
||||
*
|
||||
* @return строковое представление объекта
|
||||
*/
|
||||
public function toString():String {
|
||||
var res:String = "[Face ID:" + id + ((_num > 0) ? " vertices:" : "");
|
||||
for (var i:uint = 0; i < _num; i++) {
|
||||
var vertex:Vertex = _vertices[i];
|
||||
res += vertex.id + ((i < _num - 1) ? ", " : "");
|
||||
}
|
||||
res += "]";
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,985 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.engine3d.errors.FaceExistsError;
|
||||
import alternativa.engine3d.errors.FaceNeedMoreVerticesError;
|
||||
import alternativa.engine3d.errors.FaceNotFoundError;
|
||||
import alternativa.engine3d.errors.InvalidIDError;
|
||||
import alternativa.engine3d.errors.SurfaceExistsError;
|
||||
import alternativa.engine3d.errors.SurfaceNotFoundError;
|
||||
import alternativa.engine3d.errors.VertexExistsError;
|
||||
import alternativa.engine3d.errors.VertexNotFoundError;
|
||||
import alternativa.engine3d.materials.SurfaceMaterial;
|
||||
import alternativa.types.Map;
|
||||
import alternativa.utils.ObjectUtils;
|
||||
|
||||
import flash.geom.Point;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Полигональная сетка - базовый класс для трёхмерных объектов, состоящих из набора вершин и граней.
|
||||
*/
|
||||
public class Mesh extends Object3D {
|
||||
|
||||
// Инкремент количества объектов
|
||||
private static var counter:uint = 0;
|
||||
|
||||
// Инкременты для идентификаторов вершин, граней и поверхностей
|
||||
private var vertexIdCounter:uint = 0;
|
||||
private var faceIdCounter:uint = 0;
|
||||
private var surfaceIdCounter:uint = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Список вершин
|
||||
*/
|
||||
alternativa3d var _vertices:Map = new Map();
|
||||
/**
|
||||
* @private
|
||||
* Список граней
|
||||
*/
|
||||
alternativa3d var _faces:Map = new Map();
|
||||
/**
|
||||
* @private
|
||||
* Список поверхностей
|
||||
*/
|
||||
alternativa3d var _surfaces:Map = new Map();
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param name имя экземпляра
|
||||
*/
|
||||
public function Mesh(name:String = null) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавление новой вершины.
|
||||
*
|
||||
* @param x координата X в локальной системе координат
|
||||
* @param y координата Y в локальной системе координат
|
||||
* @param z координата Z в локальной системе координат
|
||||
* @param id идентификатор вершины. В случае если указано значение <code>null</code>, идентификатор будет
|
||||
* сформирован автоматически.
|
||||
*
|
||||
* @return добавленный экземпляр вершины
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.VertexExistsError объект уже содержит вершину с указанным идентификатором
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
|
||||
*/
|
||||
public function addVertex(x:Number = 0, y:Number = 0, z:Number = 0, id:Object = null):Vertex {
|
||||
// Проверяем ID
|
||||
if (id != null) {
|
||||
// Если уже есть вершина с таким ID
|
||||
if (_vertices[id] != undefined) {
|
||||
if (_vertices[id] is Vertex) {
|
||||
throw new VertexExistsError(id, this);
|
||||
} else {
|
||||
throw new InvalidIDError(id, this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Ищем первый свободный
|
||||
while (_vertices[vertexIdCounter] != undefined) {
|
||||
vertexIdCounter++;
|
||||
}
|
||||
id = vertexIdCounter;
|
||||
}
|
||||
|
||||
// Создаём вершину
|
||||
var v:Vertex = new Vertex(x, y, z);
|
||||
|
||||
// Добавляем вершину на сцену
|
||||
if (_scene != null) {
|
||||
v.addToScene(_scene);
|
||||
}
|
||||
|
||||
// Добавляем вершину в меш
|
||||
v.addToMesh(this);
|
||||
_vertices[id] = v;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаление вершины. При удалении вершины из объекта также удаляются все грани, в которых содержится данная вершина.
|
||||
*
|
||||
* @param vertex экземпляр класса <code>alternativa.engine3d.core.Vertex</code> или идентификатор удаляемой вершины
|
||||
*
|
||||
* @return удалённый экземпляр вершины
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.VertexNotFoundError объект не содержит указанную вершину
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимый идентификатор вершины
|
||||
*/
|
||||
public function removeVertex(vertex:Object):Vertex {
|
||||
var byLink:Boolean = vertex is Vertex;
|
||||
|
||||
// Проверяем на null
|
||||
if (vertex == null) {
|
||||
throw new VertexNotFoundError(null, this);
|
||||
}
|
||||
|
||||
// Проверяем наличие вершины в меше
|
||||
if (byLink) {
|
||||
// Если удаляем по ссылке
|
||||
if (Vertex(vertex)._mesh != this) {
|
||||
// Если вершина не в меше
|
||||
throw new VertexNotFoundError(vertex, this);
|
||||
}
|
||||
} else {
|
||||
// Если удаляем по ID
|
||||
if (_vertices[vertex] == undefined) {
|
||||
// Если нет вершины с таким ID
|
||||
throw new VertexNotFoundError(vertex, this);
|
||||
} else if (!(_vertices[vertex] is Vertex)) {
|
||||
// По этому id не вершина
|
||||
throw new InvalidIDError(vertex, this);
|
||||
}
|
||||
}
|
||||
|
||||
// Находим вершину и её ID
|
||||
var v:Vertex = byLink ? Vertex(vertex) : _vertices[vertex];
|
||||
var id:Object = byLink ? getVertexId(Vertex(vertex)) : vertex;
|
||||
|
||||
// Удаляем вершину из сцены
|
||||
if (_scene != null) {
|
||||
v.removeFromScene(_scene);
|
||||
}
|
||||
|
||||
// Удаляем вершину из меша
|
||||
v.removeFromMesh(this);
|
||||
delete _vertices[id];
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавление новой грани.
|
||||
*
|
||||
* @param vertices массив вершин грани, указанных в порядке обхода лицевой стороны грани против часовой
|
||||
* стрелки. Каждый элемент массива может быть либо экземпляром класса <code>alternativa.engine3d.core.Vertex</code>,
|
||||
* либо идентификатором в наборе вершин объекта. В обоих случаях объект должен содержать указанную вершину.
|
||||
* @param id идентификатор грани. В случае если указано значение <code>null</code>, идентификатор будет
|
||||
* сформирован автоматически.
|
||||
*
|
||||
* @return экземпляр добавленной грани
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.FaceNeedMoreVerticesError в качестве массива вершин был передан
|
||||
* <code>null</code>, либо количество вершин в массиве меньше трех
|
||||
* @throws alternativa.engine3d.errors.FaceExistsError объект уже содержит грань с заданным идентификатором
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора для грани
|
||||
* @throws alternativa.engine3d.errors.VertexNotFoundError объект не содержит какую-либо вершину из входного массива
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора для вершины
|
||||
*
|
||||
* @see Vertex
|
||||
*/
|
||||
public function addFace(vertices:Array, id:Object = null):Face {
|
||||
|
||||
// Проверяем на null
|
||||
if (vertices == null) {
|
||||
throw new FaceNeedMoreVerticesError(this);
|
||||
}
|
||||
|
||||
// Проверяем ID
|
||||
if (id != null) {
|
||||
// Если уже есть грань с таким ID
|
||||
if (_faces[id] != undefined) {
|
||||
if (_faces[id] is Face) {
|
||||
throw new FaceExistsError(id, this);
|
||||
} else {
|
||||
throw new InvalidIDError(id, this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Ищем первый свободный ID
|
||||
while (_faces[faceIdCounter] != undefined) {
|
||||
faceIdCounter++;
|
||||
}
|
||||
id = faceIdCounter;
|
||||
}
|
||||
|
||||
// Проверяем количество точек
|
||||
var length:uint = vertices.length;
|
||||
if (length < 3) {
|
||||
throw new FaceNeedMoreVerticesError(this, length);
|
||||
}
|
||||
|
||||
// Проверяем и формируем список вершин
|
||||
var v:Array = new Array();
|
||||
var vertex:Vertex;
|
||||
for (var i:uint = 0; i < length; i++) {
|
||||
if (vertices[i] is Vertex) {
|
||||
// Если работаем со ссылками
|
||||
vertex = vertices[i];
|
||||
if (vertex._mesh != this) {
|
||||
// Если вершина не в меше
|
||||
throw new VertexNotFoundError(vertices[i], this);
|
||||
}
|
||||
} else {
|
||||
// Если работаем с ID
|
||||
if (_vertices[vertices[i]] == null) {
|
||||
// Если нет вершины с таким ID
|
||||
throw new VertexNotFoundError(vertices[i], this);
|
||||
} else if (!(_vertices[vertices[i]] is Vertex)) {
|
||||
// Если id зарезервировано
|
||||
throw new InvalidIDError(vertices[i],this);
|
||||
}
|
||||
vertex = _vertices[vertices[i]];
|
||||
}
|
||||
v.push(vertex);
|
||||
}
|
||||
|
||||
// Создаём грань
|
||||
var f:Face = new Face(v);
|
||||
|
||||
// Добавляем грань на сцену
|
||||
if (_scene != null) {
|
||||
f.addToScene(_scene);
|
||||
}
|
||||
|
||||
// Добавляем грань в меш
|
||||
f.addToMesh(this);
|
||||
_faces[id] = f;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаление грани.
|
||||
*
|
||||
* @param экземпляр класса <code>alternativa.engine3d.core.Face</code> или идентификатор удаляемой грани
|
||||
*
|
||||
* @return экземпляр удаленной грани
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.FaceNotFoundError объект не содержит указанную грань
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора грани
|
||||
*/
|
||||
public function removeFace(face:Object):Face {
|
||||
var byLink:Boolean = face is Face;
|
||||
|
||||
// Проверяем на null
|
||||
if (face == null) {
|
||||
throw new FaceNotFoundError(null, this);
|
||||
}
|
||||
|
||||
// Проверяем наличие грани в меше
|
||||
if (byLink) {
|
||||
// Если удаляем по ссылке
|
||||
if (Face(face)._mesh != this) {
|
||||
// Если грань не в меше
|
||||
throw new FaceNotFoundError(face, this);
|
||||
}
|
||||
} else {
|
||||
// Если удаляем по ID
|
||||
if (_faces[face] == undefined) {
|
||||
// Если нет грани с таким ID
|
||||
throw new FaceNotFoundError(face, this);
|
||||
} else if (!(_faces[face] is Face)) {
|
||||
throw new InvalidIDError(face, this);
|
||||
}
|
||||
}
|
||||
|
||||
// Находим грань и её ID
|
||||
var f:Face = byLink ? Face(face) : _faces[face] ;
|
||||
var id:Object = byLink ? getFaceId(Face(face)) : face;
|
||||
|
||||
// Удаляем грань из сцены
|
||||
if (_scene != null) {
|
||||
f.removeFromScene(_scene);
|
||||
}
|
||||
|
||||
// Удаляем вершины из грани
|
||||
f.removeVertices();
|
||||
|
||||
// Удаляем грань из поверхности
|
||||
if (f._surface != null) {
|
||||
f._surface._faces.remove(f);
|
||||
f.removeFromSurface(f._surface);
|
||||
}
|
||||
|
||||
// Удаляем грань из меша
|
||||
f.removeFromMesh(this);
|
||||
delete _faces[id];
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавление новой поверхности.
|
||||
*
|
||||
* @param faces набор граней, составляющих поверхность. Каждый элемент массива должен быть либо экземпляром класса
|
||||
* <code>alternativa.engine3d.core.Face</code>, либо идентификатором грани. В обоих случаях объект должен содержать
|
||||
* указанную грань. Если значение параметра равно <code>null</code>, то будет создана пустая поверхность. Если
|
||||
* какая-либо грань содержится в другой поверхности, она будет перенесена в новую поверхность.
|
||||
* @param id идентификатор новой поверхности. В случае если указано значение <code>null</code>, идентификатор будет
|
||||
* сформирован автоматически.
|
||||
*
|
||||
* @return экземпляр добавленной поверхности
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.SurfaceExistsError объект уже содержит поверхность с заданным идентификатором
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора поверхности
|
||||
*
|
||||
* @see Face
|
||||
*/
|
||||
public function addSurface(faces:Array = null, id:Object = null):Surface {
|
||||
// Проверяем ID
|
||||
if (id != null) {
|
||||
// Если уже есть поверхность с таким ID
|
||||
if (_surfaces[id] != undefined) {
|
||||
if (_surfaces[id] is Surface) {
|
||||
throw new SurfaceExistsError(id, this);
|
||||
} else {
|
||||
throw new InvalidIDError(id, this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Ищем первый свободный ID
|
||||
while (_surfaces[surfaceIdCounter] != undefined) {
|
||||
surfaceIdCounter++;
|
||||
}
|
||||
id = surfaceIdCounter;
|
||||
}
|
||||
|
||||
// Создаём поверхность
|
||||
var s:Surface = new Surface();
|
||||
|
||||
// Добавляем поверхность на сцену
|
||||
if (_scene != null) {
|
||||
s.addToScene(_scene);
|
||||
}
|
||||
|
||||
// Добавляем поверхность в меш
|
||||
s.addToMesh(this);
|
||||
_surfaces[id] = s;
|
||||
|
||||
// Добавляем грани, если есть
|
||||
if (faces != null) {
|
||||
var length:uint = faces.length;
|
||||
for (var i:uint = 0; i < length; i++) {
|
||||
s.addFace(faces[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаление поверхности.
|
||||
*
|
||||
* @param surface экземпляр класса <code>alternativa.engine3d.core.Face</code> или идентификатор удаляемой поверхности
|
||||
*
|
||||
* @return экземпляр удаленной поверхности
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит указанную поверхность
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора поверхности
|
||||
*/
|
||||
public function removeSurface(surface:Object):Surface {
|
||||
var byLink:Boolean = surface is Surface;
|
||||
|
||||
// Проверяем на null
|
||||
if (surface == null) {
|
||||
throw new SurfaceNotFoundError(null, this);
|
||||
}
|
||||
|
||||
// Проверяем наличие поверхности в меше
|
||||
if (byLink) {
|
||||
// Если удаляем по ссылке
|
||||
if (Surface(surface)._mesh != this) {
|
||||
// Если поверхность не в меше
|
||||
throw new SurfaceNotFoundError(surface, this);
|
||||
}
|
||||
} else {
|
||||
// Если удаляем по ID
|
||||
if (_surfaces[surface] == undefined) {
|
||||
// Если нет поверхности с таким ID
|
||||
throw new SurfaceNotFoundError(surface, this);
|
||||
} else if (!(_surfaces[surface] is Surface)) {
|
||||
throw new InvalidIDError(surface, this);
|
||||
}
|
||||
}
|
||||
|
||||
// Находим поверхность и её ID
|
||||
var s:Surface = byLink ? Surface(surface) : _surfaces[surface];
|
||||
var id:Object = byLink ? getSurfaceId(Surface(surface)) : surface;
|
||||
|
||||
// Удаляем поверхность из сцены
|
||||
if (_scene != null) {
|
||||
s.removeFromScene(_scene);
|
||||
}
|
||||
|
||||
// Удаляем грани из поверхности
|
||||
s.removeFaces();
|
||||
|
||||
// Удаляем поверхность из меша
|
||||
s.removeFromMesh(this);
|
||||
delete _surfaces[id];
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавление всех граней полигональной сетки в указанную поверхность.
|
||||
*
|
||||
* @param surface экземпляр класса <code>alternativa.engine3d.core.Surface</code> или идентификатор поверхности, в
|
||||
* которую добавляются грани. Если задан идентификатор, и объект не содержит поверхность с таким идентификатором,
|
||||
* будет создана новая поверхность.
|
||||
*
|
||||
* @param removeSurfaces удалять или нет пустые поверхности после переноса граней
|
||||
*
|
||||
* @return экземпляр поверхности, в которую перенесены грани
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит указанный экземпляр поверхности
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора поверхности
|
||||
*/
|
||||
public function addAllFacesToSurface(surface:Object = null, removeSurfaces:Boolean = false):Surface {
|
||||
var returnSurface:Surface;
|
||||
var returnSurfaceId:Object;
|
||||
if (surface is Surface) {
|
||||
// Работаем с экземпляром Surface
|
||||
if (surface._mesh == this) {
|
||||
returnSurface = Surface(surface);
|
||||
} else {
|
||||
throw new SurfaceNotFoundError(surface, this);
|
||||
}
|
||||
} else {
|
||||
// Работаем с идентификатором
|
||||
if (_surfaces[surface] == undefined) {
|
||||
// Поверхности еще нет
|
||||
returnSurface = addSurface(null, surface);
|
||||
returnSurfaceId = surface;
|
||||
} else {
|
||||
if (_surfaces[surface] is Surface) {
|
||||
returnSurface = _surfaces[surface];
|
||||
} else {
|
||||
// _surfaces[surface] по идентификатору возвращает не Surface
|
||||
throw new InvalidIDError(surface, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Перемещаем все грани
|
||||
for each (var face:Face in _faces) {
|
||||
if (face._surface != returnSurface) {
|
||||
returnSurface.addFace(face);
|
||||
}
|
||||
}
|
||||
if (removeSurfaces) {
|
||||
// Удаляем старые, теперь вручную - меньше проверок, но рискованно
|
||||
if (returnSurfaceId == null) {
|
||||
returnSurfaceId = getSurfaceId(returnSurface);
|
||||
}
|
||||
var newSurfaces:Map = new Map();
|
||||
newSurfaces[returnSurfaceId] = returnSurface;
|
||||
delete _surfaces[returnSurfaceId];
|
||||
// Удаляем оставшиеся
|
||||
for (var currentSurfaceId:* in _surfaces) {
|
||||
// Удаляем поверхность из сцены
|
||||
var currentSurface:Surface = _surfaces[currentSurfaceId];
|
||||
if (_scene != null) {
|
||||
currentSurface.removeFromScene(_scene);
|
||||
}
|
||||
// Удаляем поверхность из меша
|
||||
currentSurface.removeFromMesh(this);
|
||||
delete _surfaces[currentSurfaceId];
|
||||
}
|
||||
// Новый список граней
|
||||
_surfaces = newSurfaces;
|
||||
}
|
||||
return returnSurface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Установка материала для указанной поверхности.
|
||||
*
|
||||
* @param material материал, назначаемый поверхности. Один экземпляр SurfaceMaterial можно назначить только одной поверхности.
|
||||
* @param surface экземпляр класса <code>alternativa.engine3d.core.Surface</code> или идентификатор поверхности
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит указанную поверхность
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора поверхности
|
||||
*
|
||||
* @see Surface
|
||||
*/
|
||||
public function setMaterialToSurface(material:SurfaceMaterial, surface:Object):void {
|
||||
var byLink:Boolean = surface is Surface;
|
||||
|
||||
// Проверяем на null
|
||||
if (surface == null) {
|
||||
throw new SurfaceNotFoundError(null, this);
|
||||
}
|
||||
|
||||
// Проверяем наличие поверхности в меше
|
||||
if (byLink) {
|
||||
// Если назначаем по ссылке
|
||||
if (Surface(surface)._mesh != this) {
|
||||
// Если поверхность не в меше
|
||||
throw new SurfaceNotFoundError(surface, this);
|
||||
}
|
||||
} else {
|
||||
// Если назначаем по ID
|
||||
if (_surfaces[surface] == undefined) {
|
||||
// Если нет поверхности с таким ID
|
||||
throw new SurfaceNotFoundError(surface, this);
|
||||
} else if (!(_surfaces[surface] is Surface)) {
|
||||
throw new InvalidIDError(surface, this);
|
||||
}
|
||||
}
|
||||
|
||||
// Находим поверхность
|
||||
var s:Surface = byLink ? Surface(surface) : _surfaces[surface];
|
||||
|
||||
// Назначаем материал
|
||||
s.material = material;
|
||||
}
|
||||
|
||||
/**
|
||||
* Установка материала для всех поверхностей объекта. Первой поверхности назначается указанный материал, всем остальным
|
||||
* назначаются его клоны.
|
||||
*
|
||||
* @param material устанавливаемый материал
|
||||
*/
|
||||
public function setMaterialToAllSurfaces(material:SurfaceMaterial):void {
|
||||
var firstSurface:Boolean = true;
|
||||
for each (var surface:Surface in _surfaces) {
|
||||
if (firstSurface) {
|
||||
firstSurface = false;
|
||||
surface.material = material;
|
||||
} else {
|
||||
surface.material = (material != null) ? SurfaceMaterial(material.clone()) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Установка UV-координат для указанной грани. Матрица преобразования UV-координат расчитывается по UV-координатам
|
||||
* первых трёх вершин грани.
|
||||
*
|
||||
* @param aUV UV-координаты, соответствующие первой вершине
|
||||
* @param bUV UV-координаты, соответствующие второй вершине
|
||||
* @param cUV UV-координаты, соответствующие третьей вершине
|
||||
* @param face экземпляр класса <code>alternativa.engine3d.core.Face</code> или идентификатор грани
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.FaceNotFoundError объект не содержит указанную грань
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора грани
|
||||
*
|
||||
* @see Face
|
||||
*/
|
||||
public function setUVsToFace(aUV:Point, bUV:Point, cUV:Point, face:Object):void {
|
||||
var byLink:Boolean = face is Face;
|
||||
|
||||
// Проверяем на null
|
||||
if (face == null) {
|
||||
throw new FaceNotFoundError(null, this);
|
||||
}
|
||||
|
||||
// Проверяем наличие грани в меше
|
||||
if (byLink) {
|
||||
// Если назначаем по ссылке
|
||||
if (Face(face)._mesh != this) {
|
||||
// Если грань не в меше
|
||||
throw new FaceNotFoundError(face, this);
|
||||
}
|
||||
} else {
|
||||
// Если назначаем по ID
|
||||
if (_faces[face] == undefined) {
|
||||
// Если нет грани с таким ID
|
||||
throw new FaceNotFoundError(face, this);
|
||||
} else if (!(_faces[face] is Face)) {
|
||||
throw new InvalidIDError(face, this);
|
||||
}
|
||||
}
|
||||
|
||||
// Находим грань
|
||||
var f:Face = byLink ? Face(face) : _faces[face];
|
||||
|
||||
// Назначаем UV-координаты
|
||||
f.aUV = aUV;
|
||||
f.bUV = bUV;
|
||||
f.cUV = cUV;
|
||||
}
|
||||
|
||||
/**
|
||||
* Набор вершин объекта. Ключами ассоциативного массива являются идентификаторы вершин, а значения вершинами.
|
||||
*/
|
||||
public function get vertices():Map {
|
||||
return _vertices.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Набор граней объекта. Ключами ассоциативного массива являются идентификаторы граней, а значения гранями.
|
||||
*/
|
||||
public function get faces():Map {
|
||||
return _faces.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Набор поверхностей объекта. Ключами ассоциативного массива являются идентификаторы поверхностей, а значения поверхностями.
|
||||
*/
|
||||
public function get surfaces():Map {
|
||||
return _surfaces.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение вершины по её идентификатору.
|
||||
*
|
||||
* @param id идентификатор вершины
|
||||
*
|
||||
* @return экземпляр вершины с идентификатором id
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.VertexNotFoundError объект не содержит вершину с указанным идентификатором
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора вершины
|
||||
*/
|
||||
public function getVertexById(id:Object):Vertex {
|
||||
if (id == null) {
|
||||
throw new VertexNotFoundError(null, this);
|
||||
}
|
||||
if (_vertices[id] == undefined) {
|
||||
// Если нет вершины с таким ID
|
||||
throw new VertexNotFoundError(id, this);
|
||||
} else {
|
||||
if (_vertices[id] is Vertex) {
|
||||
return _vertices[id];
|
||||
} else {
|
||||
throw new InvalidIDError(id, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение идентификатора вершины.
|
||||
*
|
||||
* @param экземпляр вершины
|
||||
*
|
||||
* @return идентификатор вершины
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.VertexNotFoundError объект не содержит указанную вершину
|
||||
*/
|
||||
public function getVertexId(vertex:Vertex):Object {
|
||||
if (vertex == null) {
|
||||
throw new VertexNotFoundError(null, this);
|
||||
}
|
||||
if (vertex._mesh != this) {
|
||||
// Если вершина не в меше
|
||||
throw new VertexNotFoundError(vertex, this);
|
||||
}
|
||||
for (var i:Object in _vertices) {
|
||||
if (_vertices[i] == vertex) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new VertexNotFoundError(vertex, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка наличия вершины в объекте.
|
||||
*
|
||||
* @param vertex экземпляр класса <code>alternativa.engine3d.core.Vertex</code> или идентификатор вершины
|
||||
*
|
||||
* @return <code>true</code>, если объект содержит указанную вершину, <code>false</code> в противном случае
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.VertexNotFoundError в качестве vertex был передан null
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора вершины
|
||||
*
|
||||
* @see Vertex
|
||||
*/
|
||||
public function hasVertex(vertex:Object):Boolean {
|
||||
if (vertex == null) {
|
||||
throw new VertexNotFoundError(null, this);
|
||||
}
|
||||
if (vertex is Vertex) {
|
||||
// Проверка вершины
|
||||
return vertex._mesh == this;
|
||||
} else {
|
||||
// Проверка ID вершины
|
||||
if (_vertices[vertex] != undefined) {
|
||||
// По этому ID есть объект
|
||||
if (_vertices[vertex] is Vertex) {
|
||||
// Объект является вершиной
|
||||
return true;
|
||||
} else {
|
||||
// ID некорректный
|
||||
throw new InvalidIDError(vertex, this);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение грани по ее идентификатору
|
||||
*
|
||||
* @param id идентификатор грани
|
||||
*
|
||||
* @return грань с идентификатором id
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.FaceNotFoundError объект не содержит грань с идентификатором id
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора грани
|
||||
*/
|
||||
public function getFaceById(id:Object):Face {
|
||||
if (id == null) {
|
||||
throw new FaceNotFoundError(null, this);
|
||||
}
|
||||
if (_faces[id] == undefined) {
|
||||
// Если нет грани с таким ID
|
||||
throw new FaceNotFoundError(id, this);
|
||||
} else {
|
||||
if (_faces[id] is Face) {
|
||||
return _faces[id];
|
||||
} else {
|
||||
throw new InvalidIDError(id, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение идентификатора грани.
|
||||
*
|
||||
* @param face экземпляр грани
|
||||
*
|
||||
* @return идентификатор указанной грани
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.FaceNotFoundError объект не содержит указанную грань
|
||||
*/
|
||||
public function getFaceId(face:Face):Object {
|
||||
if (face == null) {
|
||||
throw new FaceNotFoundError(null, this);
|
||||
}
|
||||
if (face._mesh != this) {
|
||||
// Если грань не в меше
|
||||
throw new FaceNotFoundError(face, this);
|
||||
}
|
||||
for (var i:Object in _faces) {
|
||||
if (_faces[i] == face) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new FaceNotFoundError(face, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка наличия грани в объекте.
|
||||
*
|
||||
* @param face экземпляр класса <code>Face</code> или идентификатор грани
|
||||
*
|
||||
* @return <code>true</code>, если объект содержит указанную грань, <code>false</code> в противном случае
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.FaceNotFoundError в качестве face был указан null
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора грани
|
||||
*/
|
||||
public function hasFace(face:Object):Boolean {
|
||||
if (face == null) {
|
||||
throw new FaceNotFoundError(null, this);
|
||||
}
|
||||
if (face is Face) {
|
||||
// Проверка грани
|
||||
return face._mesh == this;
|
||||
} else {
|
||||
// Проверка ID грани
|
||||
if (_faces[face] != undefined) {
|
||||
// По этому ID есть объект
|
||||
if (_faces[face] is Face) {
|
||||
// Объект является гранью
|
||||
return true;
|
||||
} else {
|
||||
// ID некорректный
|
||||
throw new InvalidIDError(face, this);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение поверхности по ее идентификатору
|
||||
*
|
||||
* @param id идентификатор поверхности
|
||||
*
|
||||
* @return экземпляр поверхности с идентификатором id
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит поверхность с идентификатором id
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора поверхности
|
||||
*/
|
||||
public function getSurfaceById(id:Object):Surface {
|
||||
if (id == null) {
|
||||
throw new SurfaceNotFoundError(null, this);
|
||||
}
|
||||
if (_surfaces[id] == undefined) {
|
||||
// Если нет поверхности с таким ID
|
||||
throw new SurfaceNotFoundError(id, this);
|
||||
} else {
|
||||
if (_surfaces[id] is Surface) {
|
||||
return _surfaces[id];
|
||||
} else {
|
||||
throw new InvalidIDError(id, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение идентификатора поверхности.
|
||||
*
|
||||
* @param surface экземпляр поверхности
|
||||
*
|
||||
* @return идентификатор поверхности
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит указанную поверхность
|
||||
*/
|
||||
public function getSurfaceId(surface:Surface):Object {
|
||||
if (surface == null) {
|
||||
throw new SurfaceNotFoundError(null, this);
|
||||
}
|
||||
if (surface._mesh != this) {
|
||||
// Если поверхность не в меше
|
||||
throw new SurfaceNotFoundError(surface, this);
|
||||
}
|
||||
for (var i:Object in _surfaces) {
|
||||
if (_surfaces[i] == surface) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка наличия поверхности в объекте.
|
||||
*
|
||||
* @param surface экземпляр класса <code>Surface</code> или идентификатор поверхности
|
||||
*
|
||||
* @return <code>true</true>, если объект содержит указанную поверхность, <code>false</code> в противном случае
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.SurfaceNotFoundError в качестве surface был передан null
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора поверхности
|
||||
*/
|
||||
public function hasSurface(surface:Object):Boolean {
|
||||
if (surface == null) {
|
||||
throw new SurfaceNotFoundError(null, this);
|
||||
}
|
||||
if (surface is Surface) {
|
||||
// Проверка поверхности
|
||||
return surface._mesh == this;
|
||||
} else {
|
||||
// Проверка ID поверхности
|
||||
if (_surfaces[surface] != undefined) {
|
||||
// По этому ID есть объект
|
||||
if (_surfaces[surface] is Surface) {
|
||||
// Объект является поверхностью
|
||||
return true;
|
||||
} else {
|
||||
// ID некорректный
|
||||
throw new InvalidIDError(surface, this);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @inheritDoc
|
||||
*/
|
||||
override alternativa3d function setScene(value:Scene3D):void {
|
||||
if (_scene != value) {
|
||||
var vertex:Vertex;
|
||||
var face:Face;
|
||||
var surface:Surface;
|
||||
if (value != null) {
|
||||
// Добавить вершины на сцену
|
||||
for each (vertex in _vertices) {
|
||||
vertex.addToScene(value);
|
||||
}
|
||||
// Добавить грани на сцену
|
||||
for each (face in _faces) {
|
||||
face.addToScene(value);
|
||||
}
|
||||
// Добавить поверхности на сцену
|
||||
for each (surface in _surfaces) {
|
||||
surface.addToScene(value);
|
||||
}
|
||||
} else {
|
||||
// Удалить вершины из сцены
|
||||
for each (vertex in _vertices) {
|
||||
vertex.removeFromScene(_scene);
|
||||
}
|
||||
// Удалить грани из сцены
|
||||
for each (face in _faces) {
|
||||
face.removeFromScene(_scene);
|
||||
}
|
||||
// Удалить поверхности из сцены
|
||||
for each (surface in _surfaces) {
|
||||
surface.removeFromScene(_scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
super.setScene(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override protected function defaultName():String {
|
||||
return "mesh" + ++counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override public function toString():String {
|
||||
return "[" + ObjectUtils.getClassName(this) + " " + _name + " vertices: " + _vertices.length + " faces: " + _faces.length + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected override function createEmptyObject():Object3D {
|
||||
return new Mesh();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected override function clonePropertiesFrom(source:Object3D):void {
|
||||
super.clonePropertiesFrom(source);
|
||||
|
||||
var src:Mesh = Mesh(source);
|
||||
|
||||
var id:*;
|
||||
var len:int;
|
||||
var i:int;
|
||||
// Копирование вершин
|
||||
var vertexMap:Map = new Map(true);
|
||||
for (id in src._vertices) {
|
||||
var sourceVertex:Vertex = src._vertices[id];
|
||||
vertexMap[sourceVertex] = addVertex(sourceVertex.x, sourceVertex.y, sourceVertex.z, id);
|
||||
}
|
||||
|
||||
// Копирование граней
|
||||
var faceMap:Map = new Map(true);
|
||||
for (id in src._faces) {
|
||||
var sourceFace:Face = src._faces[id];
|
||||
len = sourceFace._vertices.length;
|
||||
var faceVertices:Array = new Array(len);
|
||||
for (i = 0; i < len; i++) {
|
||||
faceVertices[i] = vertexMap[sourceFace._vertices[i]];
|
||||
}
|
||||
var newFace:Face = addFace(faceVertices, id);
|
||||
newFace.aUV = sourceFace._aUV;
|
||||
newFace.bUV = sourceFace._bUV;
|
||||
newFace.cUV = sourceFace._cUV;
|
||||
faceMap[sourceFace] = newFace;
|
||||
}
|
||||
|
||||
// Копирование поверхностей
|
||||
for (id in src._surfaces) {
|
||||
var sourceSurface:Surface = src._surfaces[id];
|
||||
var surfaceFaces:Array = sourceSurface._faces.toArray();
|
||||
len = surfaceFaces.length;
|
||||
for (i = 0; i < len; i++) {
|
||||
surfaceFaces[i] = faceMap[surfaceFaces[i]];
|
||||
}
|
||||
addSurface(surfaceFaces, id).material = SurfaceMaterial(sourceSurface.material.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,696 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.engine3d.errors.Object3DHierarchyError;
|
||||
import alternativa.engine3d.errors.Object3DNotFoundError;
|
||||
import alternativa.types.Matrix3D;
|
||||
import alternativa.types.Point3D;
|
||||
import alternativa.types.Set;
|
||||
import alternativa.utils.ObjectUtils;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Базовый класс для объектов, находящихся в сцене. Класс реализует иерархию объектов сцены, а также содержит сведения
|
||||
* о положении объекта в системе координат родителя.
|
||||
*/
|
||||
public class Object3D {
|
||||
// Операции
|
||||
/**
|
||||
* @private
|
||||
* Поворот или масштабирование
|
||||
*/
|
||||
alternativa3d var changeRotationOrScaleOperation:Operation = new Operation("changeRotationOrScale", this);
|
||||
/**
|
||||
* @private
|
||||
* Перемещение
|
||||
*/
|
||||
alternativa3d var changeCoordsOperation:Operation = new Operation("changeCoords", this);
|
||||
/**
|
||||
* @private
|
||||
* Расчёт матрицы трансформации
|
||||
*/
|
||||
alternativa3d var calculateTransformationOperation:Operation = new Operation("calculateTransformation", this, calculateTransformation, Operation.OBJECT_CALCULATE_TRANSFORMATION);
|
||||
/**
|
||||
* @private
|
||||
* Изменение уровеня мобильности
|
||||
*/
|
||||
alternativa3d var calculateMobilityOperation:Operation = new Operation("calculateMobility", this, calculateMobility, Operation.OBJECT_CALCULATE_MOBILITY);
|
||||
|
||||
// Инкремент количества объектов
|
||||
private static var counter:uint = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Наименование
|
||||
*/
|
||||
alternativa3d var _name:String;
|
||||
/**
|
||||
* @private
|
||||
* Сцена
|
||||
*/
|
||||
alternativa3d var _scene:Scene3D;
|
||||
/**
|
||||
* @private
|
||||
* Родительский объект
|
||||
*/
|
||||
alternativa3d var _parent:Object3D;
|
||||
/**
|
||||
* @private
|
||||
* Дочерние объекты
|
||||
*/
|
||||
alternativa3d var _children:Set = new Set();
|
||||
/**
|
||||
* @private
|
||||
* Уровень мобильности
|
||||
*/
|
||||
alternativa3d var _mobility:int = 0;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var inheritedMobility:int;
|
||||
/**
|
||||
* @private
|
||||
* Координаты объекта относительно родителя
|
||||
*/
|
||||
alternativa3d var _coords:Point3D = new Point3D();
|
||||
/**
|
||||
* @private
|
||||
* Поворот объекта по оси X относительно родителя. Угол измеряется в радианах.
|
||||
*/
|
||||
alternativa3d var _rotationX:Number = 0;
|
||||
/**
|
||||
* @private
|
||||
* Поворот объекта по оси Y относительно родителя. Угол измеряется в радианах.
|
||||
*/
|
||||
alternativa3d var _rotationY:Number = 0;
|
||||
/**
|
||||
* @private
|
||||
* Поворот объекта по оси Z относительно родителя. Угол измеряется в радианах.
|
||||
*/
|
||||
alternativa3d var _rotationZ:Number = 0;
|
||||
/**
|
||||
* @private
|
||||
* Мастшаб объекта по оси X относительно родителя
|
||||
*/
|
||||
alternativa3d var _scaleX:Number = 1;
|
||||
/**
|
||||
* @private
|
||||
* Мастшаб объекта по оси Y относительно родителя
|
||||
*/
|
||||
alternativa3d var _scaleY:Number = 1;
|
||||
/**
|
||||
* @private
|
||||
* Мастшаб объекта по оси Z относительно родителя
|
||||
*/
|
||||
alternativa3d var _scaleZ:Number = 1;
|
||||
/**
|
||||
* @private
|
||||
* Полная матрица трансформации, переводящая координаты из локальной системы координат объекта в систему координат сцены
|
||||
*/
|
||||
alternativa3d var transformation:Matrix3D = new Matrix3D();
|
||||
/**
|
||||
* @private
|
||||
* Координаты в сцене
|
||||
*/
|
||||
alternativa3d var globalCoords:Point3D = new Point3D();
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param name имя объекта
|
||||
*/
|
||||
public function Object3D(name:String = null) {
|
||||
// Имя по-умолчанию
|
||||
_name = (name != null) ? name : defaultName();
|
||||
|
||||
// Последствия операций
|
||||
changeRotationOrScaleOperation.addSequel(calculateTransformationOperation);
|
||||
changeCoordsOperation.addSequel(calculateTransformationOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчёт трансформации
|
||||
*/
|
||||
private function calculateTransformation():void {
|
||||
if (changeRotationOrScaleOperation.queued) {
|
||||
// Если полная трансформация
|
||||
transformation.toTransform(_coords.x, _coords.y, _coords.z, _rotationX, _rotationY, _rotationZ, _scaleX, _scaleY, _scaleZ);
|
||||
if (_parent != null) {
|
||||
transformation.combine(_parent.transformation);
|
||||
}
|
||||
// Сохраняем глобальные координаты объекта
|
||||
globalCoords.x = transformation.d;
|
||||
globalCoords.y = transformation.h;
|
||||
globalCoords.z = transformation.l;
|
||||
} else {
|
||||
// Если только перемещение
|
||||
globalCoords.copy(_coords);
|
||||
if (_parent != null) {
|
||||
globalCoords.transform(_parent.transformation);
|
||||
}
|
||||
transformation.offset(globalCoords.x, globalCoords.y, globalCoords.z);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчёт общей мобильности
|
||||
*/
|
||||
private function calculateMobility():void {
|
||||
inheritedMobility = ((_parent != null) ? _parent.inheritedMobility : 0) + _mobility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавление дочернего объекта.
|
||||
* Добавляемый объект удаляется из списка детей предыдущего родителя.
|
||||
* Новой сценой дочернего объекта становится сцена родителя.
|
||||
*
|
||||
* @param child дочерний объект
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.Object3DLevelError попытка добавить дочерний объект, который не может быть связан с данным родителем
|
||||
*/
|
||||
public function addChild(child:Object3D):void {
|
||||
|
||||
// Проверка на null
|
||||
if (child == null) {
|
||||
throw new Object3DHierarchyError(null, this);
|
||||
}
|
||||
|
||||
// Проверка на наличие
|
||||
if (child._parent == this) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Проверка на добавление к самому себе
|
||||
if (child == this) {
|
||||
throw new Object3DHierarchyError(this, this);
|
||||
}
|
||||
|
||||
// Проверка на добавление родительского объекта
|
||||
if (child._scene == _scene) {
|
||||
// Если объект был в той же сцене, либо оба не были в сцене
|
||||
var parentObject:Object3D = _parent;
|
||||
while (parentObject != null) {
|
||||
if (child == parentObject) {
|
||||
throw new Object3DHierarchyError(child, this);
|
||||
return;
|
||||
}
|
||||
parentObject = parentObject._parent;
|
||||
}
|
||||
}
|
||||
|
||||
// Если объект был в другом объекте
|
||||
if (child._parent != null) {
|
||||
// Удалить его оттуда
|
||||
child._parent._children.remove(child);
|
||||
} else {
|
||||
// Если объект был корневым в сцене
|
||||
if (child._scene != null && child._scene._root == child) {
|
||||
child._scene.root = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Добавляем в список
|
||||
_children.add(child);
|
||||
// Указываем себя как родителя
|
||||
child.setParent(this);
|
||||
// Устанавливаем уровни
|
||||
child.setLevel(calculateTransformationOperation.level + 1);
|
||||
// Указываем сцену
|
||||
child.setScene(_scene);
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаление дочернего объекта.
|
||||
*
|
||||
* @param child дочерний объект
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.Object3DNotFoundError попытка удалить несуществующий дочерний объект
|
||||
*/
|
||||
public function removeChild(child:Object3D):void {
|
||||
// Проверка на null
|
||||
if (child == null) {
|
||||
throw new Object3DNotFoundError(null, this);
|
||||
}
|
||||
// Проверка на наличие
|
||||
if (child._parent != this) {
|
||||
throw new Object3DNotFoundError(child, this);
|
||||
}
|
||||
// Убираем из списка
|
||||
_children.remove(child);
|
||||
// Удаляем ссылку на родителя
|
||||
child.setParent(null);
|
||||
// Удаляем ссылку на сцену
|
||||
child.setScene(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Установка родительского объекта.
|
||||
*
|
||||
* @param value родительский объект
|
||||
*/
|
||||
alternativa3d function setParent(value:Object3D):void {
|
||||
// Отписываемся от сигналов старого родителя
|
||||
if (_parent != null) {
|
||||
removeParentSequels();
|
||||
}
|
||||
// Сохранить родителя
|
||||
_parent = value;
|
||||
// Если устанавливаем родителя
|
||||
if (value != null) {
|
||||
// Подписка на сигналы родителя
|
||||
addParentSequels();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Установка новой сцены для объекта.
|
||||
*
|
||||
* @param value сцена
|
||||
*/
|
||||
alternativa3d function setScene(value:Scene3D):void {
|
||||
if (_scene != value) {
|
||||
// Если была сцена
|
||||
if (_scene != null) {
|
||||
// Удалиться из сцены
|
||||
removeFromScene(_scene);
|
||||
}
|
||||
// Если новая сцена
|
||||
if (value != null) {
|
||||
// Добавиться на сцену
|
||||
addToScene(value);
|
||||
}
|
||||
// Сохранить сцену
|
||||
_scene = value;
|
||||
} else {
|
||||
// Посылаем операцию трансформации
|
||||
addOperationToScene(changeRotationOrScaleOperation);
|
||||
// Посылаем операцию пересчёта мобильности
|
||||
addOperationToScene(calculateMobilityOperation);
|
||||
}
|
||||
// Установить эту сцену у дочерних объектов
|
||||
for (var key:* in _children) {
|
||||
var object:Object3D = key;
|
||||
object.setScene(_scene);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Установка уровня операции трансформации.
|
||||
*
|
||||
* @param value уровень операции трансформации
|
||||
*/
|
||||
alternativa3d function setLevel(value:uint):void {
|
||||
// Установить уровень операции трансформации и расчёта мобильности
|
||||
calculateTransformationOperation.level = value;
|
||||
calculateMobilityOperation.level = value;
|
||||
// Установить уровни у дочерних объектов
|
||||
for (var key:* in _children) {
|
||||
var object:Object3D = key;
|
||||
object.setLevel(value + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Подписка на сигналы родителя.
|
||||
*/
|
||||
private function addParentSequels():void {
|
||||
_parent.changeCoordsOperation.addSequel(changeCoordsOperation);
|
||||
_parent.changeRotationOrScaleOperation.addSequel(changeRotationOrScaleOperation);
|
||||
_parent.calculateMobilityOperation.addSequel(calculateMobilityOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление подписки на сигналы родителя.
|
||||
*/
|
||||
private function removeParentSequels():void {
|
||||
_parent.changeCoordsOperation.removeSequel(changeCoordsOperation);
|
||||
_parent.changeRotationOrScaleOperation.removeSequel(changeRotationOrScaleOperation);
|
||||
_parent.calculateMobilityOperation.removeSequel(calculateMobilityOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод вызывается при добавлении объекта на сцену. Наследники могут переопределять метод для выполнения
|
||||
* специфических действий.
|
||||
*
|
||||
* @param scene сцена, в которую добавляется объект
|
||||
*/
|
||||
protected function addToScene(scene:Scene3D):void {
|
||||
// При добавлении на сцену полная трансформация и расчёт мобильности
|
||||
scene.addOperation(changeRotationOrScaleOperation);
|
||||
scene.addOperation(calculateMobilityOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод вызывается при удалении объекта со сцены. Наследники могут переопределять метод для выполнения
|
||||
* специфических действий.
|
||||
*
|
||||
* @param scene сцена, из которой удаляется объект
|
||||
*/
|
||||
protected function removeFromScene(scene:Scene3D):void {
|
||||
// Удаляем все операции из очереди
|
||||
scene.removeOperation(changeRotationOrScaleOperation);
|
||||
scene.removeOperation(changeCoordsOperation);
|
||||
scene.removeOperation(calculateMobilityOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Наименование объекта.
|
||||
*/
|
||||
public function get name():String {
|
||||
return _name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set name(value:String):void {
|
||||
_name = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Сцена, на которой находится объект.
|
||||
*/
|
||||
public function get scene():Scene3D {
|
||||
return _scene;
|
||||
}
|
||||
|
||||
/**
|
||||
* Родительский объект.
|
||||
*/
|
||||
public function get parent():Object3D {
|
||||
return _parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Набор дочерних объектов.
|
||||
*/
|
||||
public function get children():Set {
|
||||
return _children.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Уровень мобильности. Результирующая мобильность объекта является суммой мобильностей объекта и всех его предков
|
||||
* по иерархии объектов в сцене.
|
||||
*/
|
||||
public function get mobility():int {
|
||||
return _mobility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set mobility(value:int):void {
|
||||
if (_mobility != value) {
|
||||
_mobility = value;
|
||||
addOperationToScene(calculateMobilityOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Координата X.
|
||||
*/
|
||||
public function get x():Number {
|
||||
return _coords.x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Координата Y.
|
||||
*/
|
||||
public function get y():Number {
|
||||
return _coords.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Координата Z.
|
||||
*/
|
||||
public function get z():Number {
|
||||
return _coords.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set x(value:Number):void {
|
||||
if (_coords.x != value) {
|
||||
_coords.x = value;
|
||||
addOperationToScene(changeCoordsOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set y(value:Number):void {
|
||||
if (_coords.y != value) {
|
||||
_coords.y = value;
|
||||
addOperationToScene(changeCoordsOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set z(value:Number):void {
|
||||
if (_coords.z != value) {
|
||||
_coords.z = value;
|
||||
addOperationToScene(changeCoordsOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Координаты объекта.
|
||||
*/
|
||||
public function get coords():Point3D {
|
||||
return _coords.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set coords(value:Point3D):void {
|
||||
if (!_coords.equals(value)) {
|
||||
_coords.copy(value);
|
||||
addOperationToScene(changeCoordsOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Угол поворота вокруг оси X, заданный в радианах.
|
||||
*/
|
||||
public function get rotationX():Number {
|
||||
return _rotationX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Угол поворота вокруг оси Y, заданный в радианах.
|
||||
*/
|
||||
public function get rotationY():Number {
|
||||
return _rotationY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Угол поворота вокруг оси Z, заданный в радианах.
|
||||
*/
|
||||
public function get rotationZ():Number {
|
||||
return _rotationZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set rotationX(value:Number):void {
|
||||
if (_rotationX != value) {
|
||||
_rotationX = value;
|
||||
addOperationToScene(changeRotationOrScaleOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set rotationY(value:Number):void {
|
||||
if (_rotationY != value) {
|
||||
_rotationY = value;
|
||||
addOperationToScene(changeRotationOrScaleOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set rotationZ(value:Number):void {
|
||||
if (_rotationZ != value) {
|
||||
_rotationZ = value;
|
||||
addOperationToScene(changeRotationOrScaleOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Коэффициент масштабирования вдоль оси X.
|
||||
*/
|
||||
public function get scaleX():Number {
|
||||
return _scaleX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Коэффициент масштабирования вдоль оси Y.
|
||||
*/
|
||||
public function get scaleY():Number {
|
||||
return _scaleY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Коэффициент масштабирования вдоль оси Z.
|
||||
*/
|
||||
public function get scaleZ():Number {
|
||||
return _scaleZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set scaleX(value:Number):void {
|
||||
if (_scaleX != value) {
|
||||
_scaleX = value;
|
||||
addOperationToScene(changeRotationOrScaleOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set scaleY(value:Number):void {
|
||||
if (_scaleY != value) {
|
||||
_scaleY = value;
|
||||
addOperationToScene(changeRotationOrScaleOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set scaleZ(value:Number):void {
|
||||
if (_scaleZ != value) {
|
||||
_scaleZ = value;
|
||||
addOperationToScene(changeRotationOrScaleOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Строковое представление объекта.
|
||||
*
|
||||
* @return строковое представление объекта
|
||||
*/
|
||||
public function toString():String {
|
||||
return "[" + ObjectUtils.getClassName(this) + " " + _name + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Имя объекта по умолчанию.
|
||||
*
|
||||
* @return имя объекта по умолчанию
|
||||
*/
|
||||
protected function defaultName():String {
|
||||
return "object" + ++counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Добавление операции в очередь.
|
||||
*
|
||||
* @param operation добавляемая операция
|
||||
*/
|
||||
alternativa3d function addOperationToScene(operation:Operation):void {
|
||||
if (_scene != null) {
|
||||
_scene.addOperation(operation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление операции из очереди.
|
||||
*
|
||||
* @param operation удаляемая операция
|
||||
*/
|
||||
alternativa3d function removeOperationFromScene(operation:Operation):void {
|
||||
if (_scene != null) {
|
||||
_scene.removeOperation(operation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Создание пустого объекта без какой-либо внутренней структуры. Например, если некоторый геометрический примитив при
|
||||
* своём создании формирует набор вершин, граней и поверхностей, то этот метод не должен создавать вершины, грани и
|
||||
* поверхности. Данный метод используется в методе clone() и должен быть переопределён в потомках для получения
|
||||
* правильного объекта.
|
||||
*
|
||||
* @return новый пустой объект
|
||||
*/
|
||||
protected function createEmptyObject():Object3D {
|
||||
return new Object3D();
|
||||
}
|
||||
|
||||
/**
|
||||
* Копирование свойств объекта-источника. Данный метод используется в методе clone() и должен быть переопределён в
|
||||
* потомках для получения правильного объекта. Каждый потомок должен в переопределённом методе копировать только те
|
||||
* свойства, которые добавлены к базовому классу именно в нём. Копирование унаследованных свойств выполняется
|
||||
* вызовом super.clonePropertiesFrom(source).
|
||||
*
|
||||
* @param source объект, свойства которого копируются
|
||||
*/
|
||||
protected function clonePropertiesFrom(source:Object3D):void {
|
||||
_name = source._name;
|
||||
_mobility = source._mobility;
|
||||
_coords.x = source._coords.x;
|
||||
_coords.y = source._coords.y;
|
||||
_coords.z = source._coords.z;
|
||||
_rotationX = source._rotationX;
|
||||
_rotationY = source._rotationY;
|
||||
_rotationZ = source._rotationZ;
|
||||
_scaleX = source._scaleX;
|
||||
_scaleY = source._scaleY;
|
||||
_scaleZ = source._scaleZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Клонирование объекта.
|
||||
*
|
||||
* @return клон объекта
|
||||
*/
|
||||
public function clone():Object3D {
|
||||
var copy:Object3D = createEmptyObject();
|
||||
copy.clonePropertiesFrom(this);
|
||||
|
||||
// Клонирование детей
|
||||
for (var key:* in _children) {
|
||||
var child:Object3D = key;
|
||||
copy.addChild(child.clone());
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение дочернего объекта с заданным именем.
|
||||
*
|
||||
* @param name имя дочернего объекта
|
||||
* @return любой дочерний объект с заданным именем или <code>null</code> в случае отсутствия таких объектов
|
||||
*/
|
||||
public function getChildByName(name:String):Object3D {
|
||||
for (var key:* in _children) {
|
||||
var child:Object3D = key;
|
||||
if (child._name == name) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.types.Set;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class Operation {
|
||||
|
||||
alternativa3d static const OBJECT_CALCULATE_TRANSFORMATION:uint = 1;
|
||||
alternativa3d static const OBJECT_CALCULATE_MOBILITY:uint = 2;
|
||||
alternativa3d static const VERTEX_CALCULATE_COORDS:uint = 3;
|
||||
alternativa3d static const FACE_CALCULATE_NORMAL:uint = 4;
|
||||
alternativa3d static const FACE_CALCULATE_UV:uint = 5;
|
||||
alternativa3d static const FACE_UPDATE_PRIMITIVE:uint = 6;
|
||||
alternativa3d static const SCENE_CALCULATE_BSP:uint = 7;
|
||||
alternativa3d static const FACE_UPDATE_MATERIAL:uint = 8;
|
||||
alternativa3d static const FACE_CALCULATE_FRAGMENTS_UV:uint = 9;
|
||||
alternativa3d static const CAMERA_CALCULATE_MATRIX:uint = 10;
|
||||
alternativa3d static const CAMERA_CALCULATE_PLANES:uint = 11;
|
||||
alternativa3d static const CAMERA_RENDER:uint = 12;
|
||||
alternativa3d static const SCENE_CLEAR_PRIMITIVES:uint = 13;
|
||||
|
||||
// Объект
|
||||
alternativa3d var object:Object;
|
||||
|
||||
// Метод
|
||||
alternativa3d var method:Function;
|
||||
|
||||
// Название метода
|
||||
alternativa3d var name:String;
|
||||
|
||||
// Последствия
|
||||
private var sequel:Operation;
|
||||
private var sequels:Set;
|
||||
|
||||
// Приоритет операции
|
||||
public var priority:uint;
|
||||
|
||||
// Уровень объекта (необязательный)
|
||||
public var level:uint = 0;
|
||||
|
||||
// Находится ли операция в очереди
|
||||
alternativa3d var queued:Boolean = false;
|
||||
|
||||
public function Operation(name:String, object:Object = null, method:Function = null, priority:uint = 0, level:uint = 0) {
|
||||
this.object = object;
|
||||
this.method = method;
|
||||
this.name = name;
|
||||
this.priority = priority;
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
// Добавить последствие
|
||||
alternativa3d function addSequel(operation:Operation):void {
|
||||
if (sequel == null) {
|
||||
if (sequels == null) {
|
||||
sequel = operation;
|
||||
} else {
|
||||
sequels[operation] = true;
|
||||
}
|
||||
} else {
|
||||
if (sequel != operation) {
|
||||
sequels = new Set(true);
|
||||
sequels[sequel] = true;
|
||||
sequels[operation] = true;
|
||||
sequel = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Удалить последствие
|
||||
alternativa3d function removeSequel(operation:Operation):void {
|
||||
if (sequel == null) {
|
||||
if (sequels != null) {
|
||||
delete sequels[operation];
|
||||
var key:*;
|
||||
var single:Boolean = false;
|
||||
for (key in sequels) {
|
||||
if (single) {
|
||||
single = false;
|
||||
break;
|
||||
}
|
||||
single = true;
|
||||
}
|
||||
if (single) {
|
||||
sequel = key;
|
||||
sequels = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (sequel == operation) {
|
||||
sequel = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
alternativa3d function collectSequels(collector:Array):void {
|
||||
if (sequel == null) {
|
||||
// Проверяем последствия
|
||||
for (var key:* in sequels) {
|
||||
var operation:Operation = key;
|
||||
// Если операция ещё не в очереди
|
||||
if (!operation.queued) {
|
||||
// Добавляем её в очередь
|
||||
collector.push(operation);
|
||||
// Устанавливаем флаг очереди
|
||||
operation.queued = true;
|
||||
// Вызываем добавление в очередь её последствий
|
||||
operation.collectSequels(collector);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!sequel.queued) {
|
||||
collector.push(sequel);
|
||||
sequel.queued = true;
|
||||
sequel.collectSequels(collector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function toString():String {
|
||||
return "[Operation " + priority + "/" + level + " " + object + "." + name + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.*;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class PolyPrimitive {
|
||||
|
||||
// Количество точек
|
||||
alternativa3d var num:uint;
|
||||
// Точки
|
||||
alternativa3d var points:Array = new Array();
|
||||
// UV-координаты
|
||||
alternativa3d var uvs:Array = new Array();
|
||||
|
||||
// Грань
|
||||
alternativa3d var face:Face;
|
||||
// Родительский примитив
|
||||
alternativa3d var parent:PolyPrimitive;
|
||||
// Соседний примитив (при наличии родительского)
|
||||
alternativa3d var sibling:PolyPrimitive;
|
||||
|
||||
// Фрагменты
|
||||
alternativa3d var fragment1:PolyPrimitive;
|
||||
alternativa3d var fragment2:PolyPrimitive;
|
||||
// Рассечения
|
||||
alternativa3d var splitTime1:Number;
|
||||
alternativa3d var splitTime2:Number;
|
||||
|
||||
// BSP-нода, в которой находится примитив
|
||||
alternativa3d var node:BSPNode;
|
||||
|
||||
// Значения для расчёта качества сплиттера
|
||||
alternativa3d var splits:uint;
|
||||
alternativa3d var disbalance:int;
|
||||
// Качество примитива как сплиттера (меньше - лучше)
|
||||
public var splitQuality:Number;
|
||||
|
||||
// Приоритет в BSP-дереве. Чем ниже мобильность, тем примитив выше в дереве.
|
||||
public var mobility:int;
|
||||
|
||||
// Хранилище неиспользуемых примитивов
|
||||
static private var collector:Array = new Array();
|
||||
|
||||
// Создать примитив
|
||||
static alternativa3d function createPolyPrimitive():PolyPrimitive {
|
||||
// Достаём примитив из коллектора
|
||||
var primitive:PolyPrimitive = collector.pop();
|
||||
// Если коллектор пуст, создаём новый примитив
|
||||
if (primitive == null) {
|
||||
primitive = new PolyPrimitive();
|
||||
}
|
||||
//trace(primitive.num, primitive.points.length, primitive.face, primitive.parent, primitive.sibling, primitive.fragment1, primitive.fragment2, primitive.node);
|
||||
return primitive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Кладёт примитив в коллектор для последующего реиспользования.
|
||||
* Ссылка на грань и массивы точек зачищаются в этом методе.
|
||||
* Ссылки на фрагменты (parent, sibling, back, front) должны быть зачищены перед запуском метода.
|
||||
*
|
||||
* Исключение:
|
||||
* при сборке примитивов в сцене ссылки на back и front зачищаются после запуска метода.
|
||||
*
|
||||
* @param primitive примитив на реиспользование
|
||||
*/
|
||||
static alternativa3d function destroyPolyPrimitive(primitive:PolyPrimitive):void {
|
||||
primitive.face = null;
|
||||
for (var i:uint = 0; i < primitive.num; i++) {
|
||||
primitive.points.pop();
|
||||
primitive.uvs.pop();
|
||||
}
|
||||
collector.push(primitive);
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
return "[Primitive " + face._mesh._name + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,349 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.engine3d.errors.FaceExistsError;
|
||||
import alternativa.engine3d.errors.FaceNotFoundError;
|
||||
import alternativa.engine3d.errors.InvalidIDError;
|
||||
import alternativa.engine3d.materials.SurfaceMaterial;
|
||||
import alternativa.types.Set;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Поверхность — набор граней, объединённых в группу для удобства работы с их общими свойствами.
|
||||
*/
|
||||
public class Surface {
|
||||
// Операции
|
||||
/**
|
||||
* @private
|
||||
* Изменение набора граней
|
||||
*/
|
||||
alternativa3d var changeFacesOperation:Operation = new Operation("changeFaces", this);
|
||||
/**
|
||||
* @private
|
||||
* Изменение материала
|
||||
*/
|
||||
alternativa3d var changeMaterialOperation:Operation = new Operation("changeMaterial", this);
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Меш
|
||||
*/
|
||||
alternativa3d var _mesh:Mesh;
|
||||
/**
|
||||
* @private
|
||||
* Материал
|
||||
*/
|
||||
alternativa3d var _material:SurfaceMaterial;
|
||||
/**
|
||||
* @private
|
||||
* Грани
|
||||
*/
|
||||
alternativa3d var _faces:Set = new Set();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function Surface() {}
|
||||
|
||||
/**
|
||||
* Добавление грани в поверхность.
|
||||
*
|
||||
* @param face экземпляр класса Face или идентификатор грани в Mesh-объекте
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.FaceNotFoundError грань не найдена в Mesh-оъекте
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение для идентификатора грани
|
||||
* @throws alternativa.engine3d.errors.FaceExistsError попытка добавить уже существующую грань
|
||||
*
|
||||
* @see Face
|
||||
*/
|
||||
public function addFace(face:Object):void {
|
||||
var byLink:Boolean = face is Face;
|
||||
|
||||
// Проверяем на нахождение поверхности в меше
|
||||
if (_mesh == null) {
|
||||
throw new FaceNotFoundError(face, this);
|
||||
}
|
||||
|
||||
// Проверяем на null
|
||||
if (face == null) {
|
||||
throw new FaceNotFoundError(null, this);
|
||||
}
|
||||
|
||||
// Проверяем наличие грани в меше
|
||||
if (byLink) {
|
||||
// Если удаляем по ссылке
|
||||
if (Face(face)._mesh != _mesh) {
|
||||
// Если грань не в меше
|
||||
throw new FaceNotFoundError(face, this);
|
||||
}
|
||||
} else {
|
||||
// Если удаляем по ID
|
||||
if (_mesh._faces[face] == undefined) {
|
||||
// Если нет грани с таким ID
|
||||
throw new FaceNotFoundError(face, this);
|
||||
} else {
|
||||
if (!(_mesh._faces[face] is Face)) {
|
||||
throw new InvalidIDError(face, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Находим грань
|
||||
var f:Face = byLink ? Face(face) : _mesh._faces[face];
|
||||
|
||||
// Проверяем наличие грани в поверхности
|
||||
if (_faces.has(f)) {
|
||||
// Если грань уже в поверхности
|
||||
throw new FaceExistsError(f, this);
|
||||
}
|
||||
|
||||
// Проверяем грань на нахождение в другой поверхности
|
||||
if (f._surface != null) {
|
||||
// Удаляем её из той поверхности
|
||||
f._surface._faces.remove(f);
|
||||
f.removeFromSurface(f._surface);
|
||||
}
|
||||
|
||||
// Добавляем грань в поверхность
|
||||
_faces.add(f);
|
||||
f.addToSurface(this);
|
||||
|
||||
// Отправляем операцию изменения набора граней
|
||||
_mesh.addOperationToScene(changeFacesOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаление грани из поверхности. Грань может быть задана либо экземпляром класса <code>alternativa.engine3d.core.Face</code>,
|
||||
* либо идентификатором грани в Mesh-объекте.
|
||||
*
|
||||
* @param face экземпляр класса Face или идентификатор грани.
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.FaceNotFoundError face не находится в Surface
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError в качестве face передано зарезервированное значение
|
||||
*
|
||||
* @see Face
|
||||
*/
|
||||
public function removeFace(face:Object):void {
|
||||
var byLink:Boolean = face is Face;
|
||||
|
||||
// Проверяем на нахождение поверхности в меше
|
||||
if (_mesh == null) {
|
||||
throw new FaceNotFoundError(face, this);
|
||||
}
|
||||
|
||||
// Проверяем на null
|
||||
if (face == null) {
|
||||
throw new FaceNotFoundError(null, this);
|
||||
}
|
||||
|
||||
// Проверяем наличие грани в меше
|
||||
if (byLink) {
|
||||
// Если удаляем по ссылке
|
||||
if (Face(face)._mesh != _mesh) {
|
||||
// Если грань не в меше
|
||||
throw new FaceNotFoundError(face, this);
|
||||
}
|
||||
} else {
|
||||
// Если удаляем по ID
|
||||
if (_mesh._faces[face] == undefined) {
|
||||
// Если нет грани с таким ID
|
||||
throw new FaceNotFoundError(face, this);
|
||||
} else {
|
||||
if (!(_mesh._faces[face] is Face)) {
|
||||
throw new InvalidIDError(face, this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Находим грань
|
||||
var f:Face = byLink ? Face(face) : _mesh._faces[face];
|
||||
|
||||
// Проверяем наличие грани в поверхности
|
||||
if (!_faces.has(f)) {
|
||||
// Если грань не в поверхности
|
||||
throw new FaceNotFoundError(f, this);
|
||||
}
|
||||
|
||||
// Удаляем грань из поверхности
|
||||
_faces.remove(f);
|
||||
f.removeFromSurface(this);
|
||||
|
||||
// Отправляем операцию изменения набора граней
|
||||
_mesh.addOperationToScene(changeFacesOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Материал поверхности. При установке нового значения, устанавливаемый материал будет удалён из старой поверхности.
|
||||
*/
|
||||
public function get material():SurfaceMaterial {
|
||||
return _material;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set material(value:SurfaceMaterial):void {
|
||||
if (_material != value) {
|
||||
// Если был материал
|
||||
if (_material != null) {
|
||||
// Удалить материал из поверхности
|
||||
_material.removeFromSurface(this);
|
||||
// Удалить материал из меша
|
||||
if (_mesh != null) {
|
||||
_material.removeFromMesh(_mesh);
|
||||
// Удалить материал из сцены
|
||||
if (_mesh._scene != null) {
|
||||
_material.removeFromScene(_mesh._scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Если новый материал
|
||||
if (value != null) {
|
||||
// Если материал был в другой поверхности
|
||||
if (value._surface != null) {
|
||||
// Удалить его оттуда
|
||||
value._surface.material = null;
|
||||
}
|
||||
// Добавить материал в поверхность
|
||||
value.addToSurface(this);
|
||||
// Добавить материал в меш
|
||||
if (_mesh != null) {
|
||||
value.addToMesh(_mesh);
|
||||
// Добавить материал в сцену
|
||||
if (_mesh._scene != null) {
|
||||
value.addToScene(_mesh._scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Сохраняем материал
|
||||
_material = value;
|
||||
// Отправляем операцию изменения материала
|
||||
addMaterialChangedOperationToScene();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Набор граней поверхности.
|
||||
*/
|
||||
public function get faces():Set {
|
||||
return _faces.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mesh-объект, которому принадлежит поверхность.
|
||||
*/
|
||||
public function get mesh():Mesh {
|
||||
return _mesh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Идентификатор поверхности в Mesh-объекте. В случае, если поверхность не принадлежит ни одному Mesh-объекту,
|
||||
* значение идентификатора равно <code>null</code>.
|
||||
*/
|
||||
public function get id():Object {
|
||||
return (_mesh != null) ? _mesh.getSurfaceId(this) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Добавление в сцену.
|
||||
*
|
||||
* @param scene
|
||||
*/
|
||||
alternativa3d function addToScene(scene:Scene3D):void {
|
||||
// Добавляем на сцену материал
|
||||
if (_material != null) {
|
||||
_material.addToScene(scene);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление из сцены.
|
||||
*
|
||||
* @param scene
|
||||
*/
|
||||
alternativa3d function removeFromScene(scene:Scene3D):void {
|
||||
// Удаляем все операции из очереди
|
||||
scene.removeOperation(changeFacesOperation);
|
||||
scene.removeOperation(changeMaterialOperation);
|
||||
// Удаляем из сцены материал
|
||||
if (_material != null) {
|
||||
_material.removeFromScene(scene);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Добавление к мешу
|
||||
* @param mesh
|
||||
*/
|
||||
alternativa3d function addToMesh(mesh:Mesh):void {
|
||||
// Подписка на операции меша
|
||||
|
||||
// Добавляем в меш материал
|
||||
if (_material != null) {
|
||||
_material.addToMesh(mesh);
|
||||
}
|
||||
// Сохранить меш
|
||||
_mesh = mesh;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление из меша
|
||||
*
|
||||
* @param mesh
|
||||
*/
|
||||
alternativa3d function removeFromMesh(mesh:Mesh):void {
|
||||
// Отписка от операций меша
|
||||
|
||||
// Удаляем из меша материал
|
||||
if (_material != null) {
|
||||
_material.removeFromMesh(mesh);
|
||||
}
|
||||
// Удалить ссылку на меш
|
||||
_mesh = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление граней
|
||||
*/
|
||||
alternativa3d function removeFaces():void {
|
||||
for (var key:* in _faces) {
|
||||
var face:Face = key;
|
||||
_faces.remove(face);
|
||||
face.removeFromSurface(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Изменение материала
|
||||
*/
|
||||
alternativa3d function addMaterialChangedOperationToScene():void {
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(changeMaterialOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Строковое представление объекта.
|
||||
* @return строковое представление объекта
|
||||
*/
|
||||
public function toString():String {
|
||||
var length:uint = _faces.length;
|
||||
var res:String = "[Surface ID:" + id + ((length > 0) ? " faces:" : "");
|
||||
var i:uint = 0;
|
||||
for (var key:* in _faces) {
|
||||
var face:Face = key;
|
||||
res += face.id + ((i < length - 1) ? ", " : "");
|
||||
i++;
|
||||
}
|
||||
res += "]";
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.types.Matrix3D;
|
||||
import alternativa.types.Point3D;
|
||||
import alternativa.types.Set;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Вершина полигона.
|
||||
*/
|
||||
final public class Vertex {
|
||||
// Операции
|
||||
/**
|
||||
* @private
|
||||
* Изменение локальных координат
|
||||
*/
|
||||
alternativa3d var changeCoordsOperation:Operation = new Operation("changeCoords", this);
|
||||
/**
|
||||
* @private
|
||||
* Расчёт глобальных координат
|
||||
*/
|
||||
alternativa3d var calculateCoordsOperation:Operation = new Operation("calculateCoords", this, calculateCoords, Operation.VERTEX_CALCULATE_COORDS);
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Меш
|
||||
*/
|
||||
alternativa3d var _mesh:Mesh;
|
||||
/**
|
||||
* @private
|
||||
* Координаты точки
|
||||
*/
|
||||
alternativa3d var _coords:Point3D;
|
||||
/**
|
||||
* @private
|
||||
* Грани
|
||||
*/
|
||||
alternativa3d var _faces:Set = new Set();
|
||||
/**
|
||||
* @private
|
||||
* Координаты в сцене
|
||||
*/
|
||||
alternativa3d var globalCoords:Point3D = new Point3D();
|
||||
|
||||
/**
|
||||
* Создание экземпляра вершины.
|
||||
*
|
||||
* @param x координата вершины по оси X
|
||||
* @param y координата вершины по оси Y
|
||||
* @param z координата вершины по оси Z
|
||||
*/
|
||||
public function Vertex(x:Number = 0, y:Number = 0, z:Number = 0) {
|
||||
_coords = new Point3D(x, y, z);
|
||||
|
||||
// Изменение координат инициирует пересчёт глобальных координат
|
||||
changeCoordsOperation.addSequel(calculateCoordsOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Вызывается из операции calculateCoordsOperation для расчета глобальных координат вершины
|
||||
*/
|
||||
private function calculateCoords():void {
|
||||
globalCoords.copy(_coords);
|
||||
globalCoords.transform(_mesh.transformation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set x(value:Number):void {
|
||||
if (_coords.x != value) {
|
||||
_coords.x = value;
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(changeCoordsOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set y(value:Number):void {
|
||||
if (_coords.y != value) {
|
||||
_coords.y = value;
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(changeCoordsOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set z(value:Number):void {
|
||||
if (_coords.z != value) {
|
||||
_coords.z = value;
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(changeCoordsOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set coords(value:Point3D):void {
|
||||
if (!_coords.equals(value)) {
|
||||
_coords.copy(value);
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(changeCoordsOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* координата вершины по оси X.
|
||||
*/
|
||||
public function get x():Number {
|
||||
return _coords.x;
|
||||
}
|
||||
|
||||
/**
|
||||
* координата вершины по оси Y.
|
||||
*/
|
||||
public function get y():Number {
|
||||
return _coords.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* координата вершины по оси Z.
|
||||
*/
|
||||
public function get z():Number {
|
||||
return _coords.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Координаты вершины.
|
||||
*/
|
||||
public function get coords():Point3D {
|
||||
return _coords.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mesh-объект, в котором находится вершина.
|
||||
*/
|
||||
public function get mesh():Mesh {
|
||||
return _mesh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Множество граней, которым принадлежит вершина.
|
||||
*/
|
||||
public function get faces():Set {
|
||||
return _faces.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Идентификатор вершины в Mesh-объекте. Если вершина не находится в Mesh-объекте, возвращается <code>null</code>.
|
||||
* @see #mesh
|
||||
*/
|
||||
public function get id():Object {
|
||||
return (_mesh != null) ? _mesh.getVertexId(this) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param scene
|
||||
*/
|
||||
alternativa3d function addToScene(scene:Scene3D):void {
|
||||
// При добавлении на сцену расчитать глобальные координаты
|
||||
scene.addOperation(calculateCoordsOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param scene
|
||||
*/
|
||||
alternativa3d function removeFromScene(scene:Scene3D):void {
|
||||
// Удаляем все операции из очереди
|
||||
scene.removeOperation(calculateCoordsOperation);
|
||||
scene.removeOperation(changeCoordsOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param mesh
|
||||
*/
|
||||
alternativa3d function addToMesh(mesh:Mesh):void {
|
||||
// Подписка на операции меша
|
||||
mesh.changeCoordsOperation.addSequel(calculateCoordsOperation);
|
||||
mesh.changeRotationOrScaleOperation.addSequel(calculateCoordsOperation);
|
||||
// Сохранить меш
|
||||
_mesh = mesh;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param mesh
|
||||
*/
|
||||
alternativa3d function removeFromMesh(mesh:Mesh):void {
|
||||
// Отписка от операций меша
|
||||
mesh.changeCoordsOperation.removeSequel(calculateCoordsOperation);
|
||||
mesh.changeRotationOrScaleOperation.removeSequel(calculateCoordsOperation);
|
||||
// Удалить зависимые грани
|
||||
for (var key:* in _faces) {
|
||||
var face:Face = key;
|
||||
mesh.removeFace(face);
|
||||
}
|
||||
// Удалить ссылку на меш
|
||||
_mesh = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param face
|
||||
*/
|
||||
alternativa3d function addToFace(face:Face):void {
|
||||
// Подписка грани на операции
|
||||
changeCoordsOperation.addSequel(face.calculateUVOperation);
|
||||
changeCoordsOperation.addSequel(face.calculateNormalOperation);
|
||||
// Добавить грань в список
|
||||
_faces.add(face);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param face
|
||||
*/
|
||||
alternativa3d function removeFromFace(face:Face):void {
|
||||
// Отписка грани от операций
|
||||
changeCoordsOperation.removeSequel(face.calculateUVOperation);
|
||||
changeCoordsOperation.removeSequel(face.calculateNormalOperation);
|
||||
// Удалить грань из списка
|
||||
_faces.remove(face);
|
||||
}
|
||||
|
||||
/**
|
||||
* Строковое представление объекта.
|
||||
* @return строковое представление объекта
|
||||
*/
|
||||
public function toString():String {
|
||||
return "[Vertex ID:" + id + " " + _coords.x.toFixed(2) + ", " + _coords.y.toFixed(2) + ", " + _coords.z.toFixed(2) + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
65
Alternativa3D5/5.0.0/alternativa/engine3d/core/BSPNode.as
Normal file
65
Alternativa3D5/5.0.0/alternativa/engine3d/core/BSPNode.as
Normal file
@@ -0,0 +1,65 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.types.Point3D;
|
||||
import alternativa.types.Set;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public final class BSPNode {
|
||||
|
||||
// Родительская нода
|
||||
alternativa3d var parent:BSPNode;
|
||||
|
||||
// Дочерние ветки
|
||||
alternativa3d var front:BSPNode;
|
||||
alternativa3d var back:BSPNode;
|
||||
|
||||
// Нормаль плоскости ноды
|
||||
alternativa3d var normal:Point3D = new Point3D();
|
||||
|
||||
// Смещение плоскости примитива
|
||||
alternativa3d var offset:Number;
|
||||
|
||||
// Минимальная мобильность ноды
|
||||
alternativa3d var mobility:int = int.MAX_VALUE;
|
||||
|
||||
// Набор примитивов в ноде
|
||||
alternativa3d var primitive:PolyPrimitive;
|
||||
alternativa3d var backPrimitives:Set;
|
||||
alternativa3d var frontPrimitives:Set;
|
||||
|
||||
// Хранилище неиспользуемых нод
|
||||
static private var collector:Array = new Array();
|
||||
|
||||
// Создать ноду на основе примитива
|
||||
static alternativa3d function createBSPNode(primitive:PolyPrimitive):BSPNode {
|
||||
// Достаём ноду из коллектора
|
||||
var node:BSPNode = collector.pop();
|
||||
// Если коллектор пуст, создаём новую ноду
|
||||
if (node == null) {
|
||||
node = new BSPNode();
|
||||
}
|
||||
|
||||
// Добавляем примитив в ноду
|
||||
node.primitive = primitive;
|
||||
// Сохраняем ноду
|
||||
primitive.node = node;
|
||||
// Сохраняем плоскость
|
||||
node.normal.copy(primitive.face.globalNormal);
|
||||
node.offset = primitive.face.globalOffset;
|
||||
// Сохраняем мобильность
|
||||
node.mobility = primitive.mobility;
|
||||
return node;
|
||||
}
|
||||
|
||||
// Удалить ноду, все ссылки должны быть почищены
|
||||
static alternativa3d function destroyBSPNode(node:BSPNode):void {
|
||||
//trace(node.back, node.front, node.parent, node.primitive, node.backPrimitives, node.frontPrimitives);
|
||||
collector.push(node);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
892
Alternativa3D5/5.0.0/alternativa/engine3d/core/Camera3D.as
Normal file
892
Alternativa3D5/5.0.0/alternativa/engine3d/core/Camera3D.as
Normal file
@@ -0,0 +1,892 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.engine3d.display.Skin;
|
||||
import alternativa.engine3d.display.View;
|
||||
import alternativa.engine3d.materials.DrawPoint;
|
||||
import alternativa.engine3d.materials.SurfaceMaterial;
|
||||
import alternativa.types.Matrix3D;
|
||||
import alternativa.types.Point3D;
|
||||
import alternativa.types.Set;
|
||||
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Point;
|
||||
import alternativa.utils.MathUtils;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Камера для отображения 3D-сцены на экране.
|
||||
*/
|
||||
public class Camera3D extends Object3D {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчёт матрицы пространства камеры
|
||||
*/
|
||||
alternativa3d var calculateMatrixOperation:Operation = new Operation("calculateMatrix", this, calculateMatrix, Operation.CAMERA_CALCULATE_MATRIX);
|
||||
/**
|
||||
* @private
|
||||
* Расчёт плоскостей отсечения
|
||||
*/
|
||||
alternativa3d var calculatePlanesOperation:Operation = new Operation("calculatePlanes", this, calculatePlanes, Operation.CAMERA_CALCULATE_PLANES);
|
||||
/**
|
||||
* @private
|
||||
* Отрисовка
|
||||
*/
|
||||
alternativa3d var renderOperation:Operation = new Operation("render", this, render, Operation.CAMERA_RENDER);
|
||||
|
||||
// Инкремент количества объектов
|
||||
private static var counter:uint = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Поле зрения
|
||||
*/
|
||||
alternativa3d var _fov:Number = Math.PI/2;
|
||||
/**
|
||||
* @private
|
||||
* Фокусное расстояние
|
||||
*/
|
||||
alternativa3d var focalLength:Number;
|
||||
/**
|
||||
* @private
|
||||
* Перспективное искажение
|
||||
*/
|
||||
alternativa3d var focalDistortion:Number;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Флаги рассчитанности UV-матриц
|
||||
*/
|
||||
alternativa3d var uvMatricesCalculated:Set = new Set(true);
|
||||
|
||||
// Всмомогательные точки для расчёта UV-матриц
|
||||
private var textureA:Point3D = new Point3D();
|
||||
private var textureB:Point3D = new Point3D();
|
||||
private var textureC:Point3D = new Point3D();
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Вид из камеры
|
||||
*/
|
||||
alternativa3d var _view:View;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Режим отрисовки
|
||||
*/
|
||||
alternativa3d var _orthographic:Boolean = false;
|
||||
private var fullDraw:Boolean;
|
||||
|
||||
// Масштаб
|
||||
private var _zoom:Number = 1;
|
||||
|
||||
// Синус половинчатого угла обзора камеры
|
||||
private var viewAngle:Number;
|
||||
|
||||
// Направление камеры
|
||||
private var direction:Point3D = new Point3D(0, 0, 1);
|
||||
|
||||
// Обратная трансформация камеры
|
||||
private var cameraMatrix:Matrix3D = new Matrix3D();
|
||||
|
||||
// Скины
|
||||
private var firstSkin:Skin;
|
||||
private var prevSkin:Skin;
|
||||
private var currentSkin:Skin;
|
||||
|
||||
// Плоскости отсечения
|
||||
private var leftPlane:Point3D = new Point3D();
|
||||
private var rightPlane:Point3D = new Point3D();
|
||||
private var topPlane:Point3D = new Point3D();
|
||||
private var bottomPlane:Point3D = new Point3D();
|
||||
private var leftOffset:Number;
|
||||
private var rightOffset:Number;
|
||||
private var topOffset:Number;
|
||||
private var bottomOffset:Number;
|
||||
|
||||
// Вспомогательные массивы точек для отрисовки
|
||||
private var points1:Array = new Array();
|
||||
private var points2:Array = new Array();
|
||||
|
||||
/**
|
||||
* Создание нового экземпляра камеры.
|
||||
*
|
||||
* @param name наименование камеры
|
||||
*/
|
||||
public function Camera3D(name:String = null) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function calculateMatrix():void {
|
||||
// Расчёт матрицы пространства камеры
|
||||
cameraMatrix.copy(transformation);
|
||||
cameraMatrix.invert();
|
||||
if (_orthographic) {
|
||||
cameraMatrix.scale(_zoom, _zoom, _zoom);
|
||||
}
|
||||
// Направление камеры
|
||||
direction.x = transformation.c;
|
||||
direction.y = transformation.g;
|
||||
direction.z = transformation.k;
|
||||
direction.normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчёт плоскостей отсечения
|
||||
*/
|
||||
private function calculatePlanes():void {
|
||||
var halfWidth:Number = _view._width*0.5;
|
||||
var halfHeight:Number = _view._height*0.5;
|
||||
|
||||
var aw:Number = transformation.a*halfWidth;
|
||||
var ew:Number = transformation.e*halfWidth;
|
||||
var iw:Number = transformation.i*halfWidth;
|
||||
var bh:Number = transformation.b*halfHeight;
|
||||
var fh:Number = transformation.f*halfHeight;
|
||||
var jh:Number = transformation.j*halfHeight;
|
||||
if (_orthographic) {
|
||||
// Расчёт плоскостей отсечения в изометрии
|
||||
aw /= _zoom;
|
||||
ew /= _zoom;
|
||||
iw /= _zoom;
|
||||
bh /= _zoom;
|
||||
fh /= _zoom;
|
||||
jh /= _zoom;
|
||||
|
||||
// Левая плоскость
|
||||
leftPlane.x = transformation.f*transformation.k - transformation.j*transformation.g;
|
||||
leftPlane.y = transformation.j*transformation.c - transformation.b*transformation.k;
|
||||
leftPlane.z = transformation.b*transformation.g - transformation.f*transformation.c;
|
||||
leftOffset = (transformation.d - aw)*leftPlane.x + (transformation.h - ew)*leftPlane.y + (transformation.l - iw)*leftPlane.z;
|
||||
|
||||
// Правая плоскость
|
||||
rightPlane.x = -leftPlane.x;
|
||||
rightPlane.y = -leftPlane.y;
|
||||
rightPlane.z = -leftPlane.z;
|
||||
rightOffset = (transformation.d + aw)*rightPlane.x + (transformation.h + ew)*rightPlane.y + (transformation.l + iw)*rightPlane.z;
|
||||
|
||||
// Верхняя плоскость
|
||||
topPlane.x = transformation.g*transformation.i - transformation.k*transformation.e;
|
||||
topPlane.y = transformation.k*transformation.a - transformation.c*transformation.i;
|
||||
topPlane.z = transformation.c*transformation.e - transformation.g*transformation.a;
|
||||
topOffset = (transformation.d - bh)*topPlane.x + (transformation.h - fh)*topPlane.y + (transformation.l - jh)*topPlane.z;
|
||||
|
||||
// Нижняя плоскость
|
||||
bottomPlane.x = -topPlane.x;
|
||||
bottomPlane.y = -topPlane.y;
|
||||
bottomPlane.z = -topPlane.z;
|
||||
bottomOffset = (transformation.d + bh)*bottomPlane.x + (transformation.h + fh)*bottomPlane.y + (transformation.l + jh)*bottomPlane.z;
|
||||
} else {
|
||||
// Вычисляем расстояние фокуса
|
||||
focalLength = Math.sqrt(_view._width*_view._width + _view._height*_view._height)*0.5/Math.tan(0.5*_fov);
|
||||
// Вычисляем минимальное (однопиксельное) искажение перспективной коррекции
|
||||
focalDistortion = 1/(focalLength*focalLength);
|
||||
|
||||
// Расчёт плоскостей отсечения в перспективе
|
||||
var cl:Number = transformation.c*focalLength;
|
||||
var gl:Number = transformation.g*focalLength;
|
||||
var kl:Number = transformation.k*focalLength;
|
||||
|
||||
// Угловые вектора пирамиды видимости
|
||||
var leftTopX:Number = -aw - bh + cl;
|
||||
var leftTopY:Number = -ew - fh + gl;
|
||||
var leftTopZ:Number = -iw - jh + kl;
|
||||
var rightTopX:Number = aw - bh + cl;
|
||||
var rightTopY:Number = ew - fh + gl;
|
||||
var rightTopZ:Number = iw - jh + kl;
|
||||
var leftBottomX:Number = -aw + bh + cl;
|
||||
var leftBottomY:Number = -ew + fh + gl;
|
||||
var leftBottomZ:Number = -iw + jh + kl;
|
||||
var rightBottomX:Number = aw + bh + cl;
|
||||
var rightBottomY:Number = ew + fh + gl;
|
||||
var rightBottomZ:Number = iw + jh + kl;
|
||||
|
||||
// Левая плоскость
|
||||
leftPlane.x = leftBottomY*leftTopZ - leftBottomZ*leftTopY;
|
||||
leftPlane.y = leftBottomZ*leftTopX - leftBottomX*leftTopZ;
|
||||
leftPlane.z = leftBottomX*leftTopY - leftBottomY*leftTopX;
|
||||
leftOffset = transformation.d*leftPlane.x + transformation.h*leftPlane.y + transformation.l*leftPlane.z;
|
||||
|
||||
// Правая плоскость
|
||||
rightPlane.x = rightTopY*rightBottomZ - rightTopZ*rightBottomY;
|
||||
rightPlane.y = rightTopZ*rightBottomX - rightTopX*rightBottomZ;
|
||||
rightPlane.z = rightTopX*rightBottomY - rightTopY*rightBottomX;
|
||||
rightOffset = transformation.d*rightPlane.x + transformation.h*rightPlane.y + transformation.l*rightPlane.z;
|
||||
|
||||
// Верхняя плоскость
|
||||
topPlane.x = leftTopY*rightTopZ - leftTopZ*rightTopY;
|
||||
topPlane.y = leftTopZ*rightTopX - leftTopX*rightTopZ;
|
||||
topPlane.z = leftTopX*rightTopY - leftTopY*rightTopX;
|
||||
topOffset = transformation.d*topPlane.x + transformation.h*topPlane.y + transformation.l*topPlane.z;
|
||||
|
||||
// Нижняя плоскость
|
||||
bottomPlane.x = rightBottomY*leftBottomZ - rightBottomZ*leftBottomY;
|
||||
bottomPlane.y = rightBottomZ*leftBottomX - rightBottomX*leftBottomZ;
|
||||
bottomPlane.z = rightBottomX*leftBottomY - rightBottomY*leftBottomX;
|
||||
bottomOffset = transformation.d*bottomPlane.x + transformation.h*bottomPlane.y + transformation.l*bottomPlane.z;
|
||||
|
||||
|
||||
// Расчёт угла конуса
|
||||
var length:Number = Math.sqrt(leftTopX*leftTopX + leftTopY*leftTopY + leftTopZ*leftTopZ);
|
||||
leftTopX /= length;
|
||||
leftTopY /= length;
|
||||
leftTopZ /= length;
|
||||
length = Math.sqrt(rightTopX*rightTopX + rightTopY*rightTopY + rightTopZ*rightTopZ);
|
||||
rightTopX /= length;
|
||||
rightTopY /= length;
|
||||
rightTopZ /= length;
|
||||
length = Math.sqrt(leftBottomX*leftBottomX + leftBottomY*leftBottomY + leftBottomZ*leftBottomZ);
|
||||
leftBottomX /= length;
|
||||
leftBottomY /= length;
|
||||
leftBottomZ /= length;
|
||||
length = Math.sqrt(rightBottomX*rightBottomX + rightBottomY*rightBottomY + rightBottomZ*rightBottomZ);
|
||||
rightBottomX /= length;
|
||||
rightBottomY /= length;
|
||||
rightBottomZ /= length;
|
||||
|
||||
viewAngle = leftTopX*direction.x + leftTopY*direction.y + leftTopZ*direction.z;
|
||||
var dot:Number = rightTopX*direction.x + rightTopY*direction.y + rightTopZ*direction.z;
|
||||
viewAngle = (dot < viewAngle) ? dot : viewAngle;
|
||||
dot = leftBottomX*direction.x + leftBottomY*direction.y + leftBottomZ*direction.z;
|
||||
viewAngle = (dot < viewAngle) ? dot : viewAngle;
|
||||
dot = rightBottomX*direction.x + rightBottomY*direction.y + rightBottomZ*direction.z;
|
||||
viewAngle = (dot < viewAngle) ? dot : viewAngle;
|
||||
|
||||
viewAngle = Math.sin(Math.acos(viewAngle));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function render():void {
|
||||
// Режим отрисовки
|
||||
fullDraw = (calculateMatrixOperation.queued || calculatePlanesOperation.queued);
|
||||
|
||||
// Очистка рассчитанных текстурных матриц
|
||||
uvMatricesCalculated.clear();
|
||||
|
||||
// Отрисовка
|
||||
prevSkin = null;
|
||||
currentSkin = firstSkin;
|
||||
renderBSPNode(_scene.bsp);
|
||||
|
||||
// Удаление ненужных скинов
|
||||
while (currentSkin != null) {
|
||||
removeCurrentSkin();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function renderBSPNode(node:BSPNode):void {
|
||||
if (node != null) {
|
||||
var primitive:*;
|
||||
var normal:Point3D = node.normal;
|
||||
var cameraAngle:Number = direction.x*normal.x + direction.y*normal.y + direction.z*normal.z;
|
||||
var cameraOffset:Number;
|
||||
if (!_orthographic) {
|
||||
cameraOffset = globalCoords.x*normal.x + globalCoords.y*normal.y + globalCoords.z*normal.z - node.offset;
|
||||
}
|
||||
if (node.primitive != null) {
|
||||
// В ноде только базовый примитив
|
||||
if (_orthographic ? (cameraAngle < 0) : (cameraOffset > 0)) {
|
||||
// Камера спереди ноды
|
||||
if (_orthographic || cameraAngle < viewAngle) {
|
||||
renderBSPNode(node.back);
|
||||
drawSkin(node.primitive);
|
||||
}
|
||||
renderBSPNode(node.front);
|
||||
} else {
|
||||
// Камера сзади ноды
|
||||
if (_orthographic || cameraAngle > -viewAngle) {
|
||||
renderBSPNode(node.front);
|
||||
}
|
||||
renderBSPNode(node.back);
|
||||
}
|
||||
} else {
|
||||
// В ноде несколько примитивов
|
||||
if (_orthographic ? (cameraAngle < 0) : (cameraOffset > 0)) {
|
||||
// Камера спереди ноды
|
||||
if (_orthographic || cameraAngle < viewAngle) {
|
||||
renderBSPNode(node.back);
|
||||
for (primitive in node.frontPrimitives) {
|
||||
drawSkin(primitive);
|
||||
}
|
||||
}
|
||||
renderBSPNode(node.front);
|
||||
} else {
|
||||
// Камера сзади ноды
|
||||
if (_orthographic || cameraAngle > -viewAngle) {
|
||||
renderBSPNode(node.front);
|
||||
for (primitive in node.backPrimitives) {
|
||||
drawSkin(primitive);
|
||||
}
|
||||
}
|
||||
renderBSPNode(node.back);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Отрисовка скина примитива
|
||||
*/
|
||||
private function drawSkin(primitive:PolyPrimitive):void {
|
||||
if (!fullDraw && currentSkin != null && currentSkin.primitive == primitive && !_scene.changedPrimitives[primitive]) {
|
||||
// Пропуск скина
|
||||
prevSkin = currentSkin;
|
||||
currentSkin = currentSkin.nextSkin;
|
||||
} else {
|
||||
// Проверка поверхности
|
||||
var surface:Surface = primitive.face._surface;
|
||||
if (surface == null) {
|
||||
return;
|
||||
}
|
||||
// Проверка материала
|
||||
var material:SurfaceMaterial = surface._material;
|
||||
if (material == null || !material.canDraw(primitive)) {
|
||||
return;
|
||||
}
|
||||
// Отсечение выходящих за окно просмотра частей
|
||||
var i:uint;
|
||||
var length:uint = primitive.num;
|
||||
var primitivePoint:Point3D;
|
||||
var primitiveUV:Point;
|
||||
var point:DrawPoint;
|
||||
var useUV:Boolean = !_orthographic && material.useUV;
|
||||
if (useUV) {
|
||||
// Формируем список точек и UV-координат полигона
|
||||
for (i = 0; i < length; i++) {
|
||||
primitivePoint = primitive.points[i];
|
||||
primitiveUV = primitive.uvs[i];
|
||||
point = points1[i];
|
||||
if (point == null) {
|
||||
points1[i] = new DrawPoint(primitivePoint.x, primitivePoint.y, primitivePoint.z, primitiveUV.x, primitiveUV.y);
|
||||
} else {
|
||||
point.x = primitivePoint.x;
|
||||
point.y = primitivePoint.y;
|
||||
point.z = primitivePoint.z;
|
||||
point.u = primitiveUV.x;
|
||||
point.v = primitiveUV.y;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Формируем список точек полигона
|
||||
for (i = 0; i < length; i++) {
|
||||
primitivePoint = primitive.points[i];
|
||||
point = points1[i];
|
||||
if (point == null) {
|
||||
points1[i] = new DrawPoint(primitivePoint.x, primitivePoint.y, primitivePoint.z);
|
||||
} else {
|
||||
point.x = primitivePoint.x;
|
||||
point.y = primitivePoint.y;
|
||||
point.z = primitivePoint.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Отсечение по левой стороне
|
||||
length = clip(length, points1, points2, leftPlane, leftOffset, useUV);
|
||||
if (length < 3) {
|
||||
return;
|
||||
}
|
||||
// Отсечение по правой стороне
|
||||
length = clip(length, points2, points1, rightPlane, rightOffset, useUV);
|
||||
if (length < 3) {
|
||||
return;
|
||||
}
|
||||
// Отсечение по верхней стороне
|
||||
length = clip(length, points1, points2, topPlane, topOffset, useUV);
|
||||
if (length < 3) {
|
||||
return;
|
||||
}
|
||||
// Отсечение по нижней стороне
|
||||
length = clip(length, points2, points1, bottomPlane, bottomOffset, useUV);
|
||||
if (length < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fullDraw || _scene.changedPrimitives[primitive]) {
|
||||
|
||||
// Если конец списка скинов
|
||||
if (currentSkin == null) {
|
||||
// Добавляем скин в конец
|
||||
addCurrentSkin();
|
||||
} else {
|
||||
if (fullDraw || _scene.changedPrimitives[currentSkin.primitive]) {
|
||||
// Очистка скина
|
||||
currentSkin.material.clear(currentSkin);
|
||||
} else {
|
||||
// Вставка скина перед текущим
|
||||
insertCurrentSkin();
|
||||
}
|
||||
}
|
||||
|
||||
// Переводим координаты в систему камеры
|
||||
var x:Number;
|
||||
var y:Number;
|
||||
var z:Number;
|
||||
for (i = 0; i < length; i++) {
|
||||
point = points1[i];
|
||||
x = point.x;
|
||||
y = point.y;
|
||||
z = point.z;
|
||||
point.x = cameraMatrix.a*x + cameraMatrix.b*y + cameraMatrix.c*z + cameraMatrix.d;
|
||||
point.y = cameraMatrix.e*x + cameraMatrix.f*y + cameraMatrix.g*z + cameraMatrix.h;
|
||||
point.z = cameraMatrix.i*x + cameraMatrix.j*y + cameraMatrix.k*z + cameraMatrix.l;
|
||||
}
|
||||
|
||||
// Назначаем скину примитив и материал
|
||||
currentSkin.primitive = primitive;
|
||||
currentSkin.material = material;
|
||||
material.draw(this, currentSkin, length, points1);
|
||||
|
||||
// Переключаемся на следующий скин
|
||||
prevSkin = currentSkin;
|
||||
currentSkin = currentSkin.nextSkin;
|
||||
|
||||
} else {
|
||||
|
||||
// Удаление ненужных скинов
|
||||
while (currentSkin != null && _scene.changedPrimitives[currentSkin.primitive]) {
|
||||
removeCurrentSkin();
|
||||
}
|
||||
|
||||
// Переключение на следующий скин
|
||||
if (currentSkin != null) {
|
||||
prevSkin = currentSkin;
|
||||
currentSkin = currentSkin.nextSkin;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Отсечение полигона плоскостью.
|
||||
*/
|
||||
private function clip(length:uint, points1:Array, points2:Array, plane:Point3D, offset:Number, calculateUV:Boolean):uint {
|
||||
var i:uint;
|
||||
var k:Number;
|
||||
var index:uint = 0;
|
||||
var point:DrawPoint;
|
||||
var point1:DrawPoint;
|
||||
var point2:DrawPoint;
|
||||
var offset1:Number;
|
||||
var offset2:Number;
|
||||
|
||||
point1 = points1[length - 1];
|
||||
offset1 = plane.x*point1.x + plane.y*point1.y + plane.z*point1.z - offset;
|
||||
|
||||
if (calculateUV) {
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
|
||||
point2 = points1[i];
|
||||
offset2 = plane.x*point2.x + plane.y*point2.y + plane.z*point2.z - offset;
|
||||
|
||||
if (offset2 > 0) {
|
||||
if (offset1 <= 0) {
|
||||
k = offset2/(offset2 - offset1);
|
||||
point = points2[index];
|
||||
if (point == null) {
|
||||
point = new DrawPoint(point2.x - (point2.x - point1.x)*k, point2.y - (point2.y - point1.y)*k, point2.z - (point2.z - point1.z)*k, point2.u - (point2.u - point1.u)*k, point2.v - (point2.v - point1.v)*k);
|
||||
points2[index] = point;
|
||||
} else {
|
||||
point.x = point2.x - (point2.x - point1.x)*k;
|
||||
point.y = point2.y - (point2.y - point1.y)*k;
|
||||
point.z = point2.z - (point2.z - point1.z)*k;
|
||||
point.u = point2.u - (point2.u - point1.u)*k;
|
||||
point.v = point2.v - (point2.v - point1.v)*k;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
point = points2[index];
|
||||
if (point == null) {
|
||||
point = new DrawPoint(point2.x, point2.y, point2.z, point2.u, point2.v);
|
||||
points2[index] = point;
|
||||
} else {
|
||||
point.x = point2.x;
|
||||
point.y = point2.y;
|
||||
point.z = point2.z;
|
||||
point.u = point2.u;
|
||||
point.v = point2.v;
|
||||
}
|
||||
index++;
|
||||
} else {
|
||||
if (offset1 > 0) {
|
||||
k = offset2/(offset2 - offset1);
|
||||
point = points2[index];
|
||||
if (point == null) {
|
||||
point = new DrawPoint(point2.x - (point2.x - point1.x)*k, point2.y - (point2.y - point1.y)*k, point2.z - (point2.z - point1.z)*k, point2.u - (point2.u - point1.u)*k, point2.v - (point2.v - point1.v)*k);
|
||||
points2[index] = point;
|
||||
} else {
|
||||
point.x = point2.x - (point2.x - point1.x)*k;
|
||||
point.y = point2.y - (point2.y - point1.y)*k;
|
||||
point.z = point2.z - (point2.z - point1.z)*k;
|
||||
point.u = point2.u - (point2.u - point1.u)*k;
|
||||
point.v = point2.v - (point2.v - point1.v)*k;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
offset1 = offset2;
|
||||
point1 = point2;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
|
||||
point2 = points1[i];
|
||||
offset2 = plane.x*point2.x + plane.y*point2.y + plane.z*point2.z - offset;
|
||||
|
||||
if (offset2 > 0) {
|
||||
if (offset1 <= 0) {
|
||||
k = offset2/(offset2 - offset1);
|
||||
point = points2[index];
|
||||
if (point == null) {
|
||||
point = new DrawPoint(point2.x - (point2.x - point1.x)*k, point2.y - (point2.y - point1.y)*k, point2.z - (point2.z - point1.z)*k);
|
||||
points2[index] = point;
|
||||
} else {
|
||||
point.x = point2.x - (point2.x - point1.x)*k;
|
||||
point.y = point2.y - (point2.y - point1.y)*k;
|
||||
point.z = point2.z - (point2.z - point1.z)*k;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
point = points2[index];
|
||||
if (point == null) {
|
||||
point = new DrawPoint(point2.x, point2.y, point2.z);
|
||||
points2[index] = point;
|
||||
} else {
|
||||
point.x = point2.x;
|
||||
point.y = point2.y;
|
||||
point.z = point2.z;
|
||||
}
|
||||
index++;
|
||||
} else {
|
||||
if (offset1 > 0) {
|
||||
k = offset2/(offset2 - offset1);
|
||||
point = points2[index];
|
||||
if (point == null) {
|
||||
point = new DrawPoint(point2.x - (point2.x - point1.x)*k, point2.y - (point2.y - point1.y)*k, point2.z - (point2.z - point1.z)*k);
|
||||
points2[index] = point;
|
||||
} else {
|
||||
point.x = point2.x - (point2.x - point1.x)*k;
|
||||
point.y = point2.y - (point2.y - point1.y)*k;
|
||||
point.z = point2.z - (point2.z - point1.z)*k;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
offset1 = offset2;
|
||||
point1 = point2;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Добавление текущего скина.
|
||||
*/
|
||||
private function addCurrentSkin():void {
|
||||
currentSkin = Skin.createSkin();
|
||||
_view.canvas.addChild(currentSkin);
|
||||
if (prevSkin == null) {
|
||||
firstSkin = currentSkin;
|
||||
} else {
|
||||
prevSkin.nextSkin = currentSkin;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Вставляем под текущий скин.
|
||||
*/
|
||||
private function insertCurrentSkin():void {
|
||||
var skin:Skin = Skin.createSkin();
|
||||
_view.canvas.addChildAt(skin, _view.canvas.getChildIndex(currentSkin));
|
||||
skin.nextSkin = currentSkin;
|
||||
if (prevSkin == null) {
|
||||
firstSkin = skin;
|
||||
} else {
|
||||
prevSkin.nextSkin = skin;
|
||||
}
|
||||
currentSkin = skin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаляет текущий скин.
|
||||
*/
|
||||
private function removeCurrentSkin():void {
|
||||
// Сохраняем следующий
|
||||
var next:Skin = currentSkin.nextSkin;
|
||||
// Удаляем из канваса
|
||||
_view.canvas.removeChild(currentSkin);
|
||||
// Очистка скина
|
||||
if (currentSkin.material != null) {
|
||||
currentSkin.material.clear(currentSkin);
|
||||
}
|
||||
// Зачищаем ссылки
|
||||
currentSkin.nextSkin = null;
|
||||
currentSkin.primitive = null;
|
||||
currentSkin.material = null;
|
||||
// Удаляем
|
||||
Skin.destroySkin(currentSkin);
|
||||
// Следующий устанавливаем текущим
|
||||
currentSkin = next;
|
||||
// Устанавливаем связь от предыдущего скина
|
||||
if (prevSkin == null) {
|
||||
firstSkin = currentSkin;
|
||||
} else {
|
||||
prevSkin.nextSkin = currentSkin;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function calculateUVMatrix(face:Face, width:uint, height:uint):void {
|
||||
|
||||
// Расчёт точек базового примитива в координатах камеры
|
||||
var point:Point3D = face.primitive.points[0];
|
||||
textureA.x = cameraMatrix.a*point.x + cameraMatrix.b*point.y + cameraMatrix.c*point.z;
|
||||
textureA.y = cameraMatrix.e*point.x + cameraMatrix.f*point.y + cameraMatrix.g*point.z;
|
||||
point = face.primitive.points[1];
|
||||
textureB.x = cameraMatrix.a*point.x + cameraMatrix.b*point.y + cameraMatrix.c*point.z;
|
||||
textureB.y = cameraMatrix.e*point.x + cameraMatrix.f*point.y + cameraMatrix.g*point.z;
|
||||
point = face.primitive.points[2];
|
||||
textureC.x = cameraMatrix.a*point.x + cameraMatrix.b*point.y + cameraMatrix.c*point.z;
|
||||
textureC.y = cameraMatrix.e*point.x + cameraMatrix.f*point.y + cameraMatrix.g*point.z;
|
||||
|
||||
// Находим AB и AC
|
||||
var abx:Number = textureB.x - textureA.x;
|
||||
var aby:Number = textureB.y - textureA.y;
|
||||
var acx:Number = textureC.x - textureA.x;
|
||||
var acy:Number = textureC.y - textureA.y;
|
||||
|
||||
// Расчёт текстурной матрицы
|
||||
var uvMatrixBase:Matrix = face.uvMatrixBase;
|
||||
var uvMatrix:Matrix = face.uvMatrix;
|
||||
uvMatrix.a = (uvMatrixBase.a*abx + uvMatrixBase.b*acx)/width;
|
||||
uvMatrix.b = (uvMatrixBase.a*aby + uvMatrixBase.b*acy)/width;
|
||||
uvMatrix.c = -(uvMatrixBase.c*abx + uvMatrixBase.d*acx)/height;
|
||||
uvMatrix.d = -(uvMatrixBase.c*aby + uvMatrixBase.d*acy)/height;
|
||||
uvMatrix.tx = (uvMatrixBase.tx + uvMatrixBase.c)*abx + (uvMatrixBase.ty + uvMatrixBase.d)*acx + textureA.x + cameraMatrix.d;
|
||||
uvMatrix.ty = (uvMatrixBase.tx + uvMatrixBase.c)*aby + (uvMatrixBase.ty + uvMatrixBase.d)*acy + textureA.y + cameraMatrix.h;
|
||||
|
||||
// Помечаем, как рассчитанную
|
||||
uvMatricesCalculated[face] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вид, в который происходит отрисовка камеры.
|
||||
*/
|
||||
public function get view():View {
|
||||
return _view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set view(value:View):void {
|
||||
if (value != _view) {
|
||||
if (_view != null) {
|
||||
_view.camera = null;
|
||||
}
|
||||
if (value != null) {
|
||||
value.camera = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Включение режима прямоугольной (изометрической) проекции.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
public function get orthographic():Boolean {
|
||||
return _orthographic;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set orthographic(value:Boolean):void {
|
||||
if (_orthographic != value) {
|
||||
// Отправляем сигнал об изменении типа камеры
|
||||
addOperationToScene(calculateMatrixOperation);
|
||||
// Сохраняем новое значение
|
||||
_orthographic = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Угол поля зрения в радианах в режиме перспективной проекции.
|
||||
*/
|
||||
public function get fov():Number {
|
||||
return _fov;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set fov(value:Number):void {
|
||||
value = (value < 0) ? 0 : ((value > (Math.PI - 0.0001)) ? (Math.PI - 0.0001) : value);
|
||||
if (_fov != value) {
|
||||
// Если перспектива
|
||||
if (!_orthographic) {
|
||||
// Отправляем сигнал об изменении плоскостей отсечения
|
||||
addOperationToScene(calculatePlanesOperation);
|
||||
}
|
||||
// Сохраняем новое значение
|
||||
_fov = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Коэффициент увеличения изображения в режиме изометрической проекции.
|
||||
*/
|
||||
public function get zoom():Number {
|
||||
return _zoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set zoom(value:Number):void {
|
||||
value = (value < 0) ? 0 : value;
|
||||
if (_zoom != value) {
|
||||
// Если изометрия
|
||||
if (_orthographic) {
|
||||
// Отправляем сигнал об изменении zoom
|
||||
addOperationToScene(calculateMatrixOperation);
|
||||
}
|
||||
// Сохраняем новое значение
|
||||
_zoom = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override protected function addToScene(scene:Scene3D):void {
|
||||
super.addToScene(scene);
|
||||
if (_view != null) {
|
||||
// Отправляем операцию расчёта плоскостей отсечения
|
||||
scene.addOperation(calculatePlanesOperation);
|
||||
// Подписываемся на сигналы сцены
|
||||
scene.changePrimitivesOperation.addSequel(renderOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override protected function removeFromScene(scene:Scene3D):void {
|
||||
super.removeFromScene(scene);
|
||||
|
||||
// Удаляем все операции из очереди
|
||||
scene.removeOperation(calculateMatrixOperation);
|
||||
scene.removeOperation(calculatePlanesOperation);
|
||||
scene.removeOperation(renderOperation);
|
||||
|
||||
if (_view != null) {
|
||||
// Отписываемся от сигналов сцены
|
||||
scene.changePrimitivesOperation.removeSequel(renderOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function addToView(view:View):void {
|
||||
// Сохраняем первый скин
|
||||
firstSkin = (view.canvas.numChildren > 0) ? Skin(view.canvas.getChildAt(0)) : null;
|
||||
|
||||
// Подписка на свои операции
|
||||
|
||||
// При изменении камеры пересчёт матрицы
|
||||
calculateTransformationOperation.addSequel(calculateMatrixOperation);
|
||||
// При изменении матрицы или FOV пересчёт плоскостей отсечения
|
||||
calculateMatrixOperation.addSequel(calculatePlanesOperation);
|
||||
// При изменении плоскостей перерисовка
|
||||
calculatePlanesOperation.addSequel(renderOperation);
|
||||
|
||||
if (_scene != null) {
|
||||
// Отправляем сигнал перерисовки
|
||||
_scene.addOperation(calculateMatrixOperation);
|
||||
// Подписываемся на сигналы сцены
|
||||
_scene.changePrimitivesOperation.addSequel(renderOperation);
|
||||
}
|
||||
|
||||
// Сохраняем вид
|
||||
_view = view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d function removeFromView(view:View):void {
|
||||
// Сброс ссылки на первый скин
|
||||
firstSkin = null;
|
||||
|
||||
// Отписка от своих операций
|
||||
|
||||
// При изменении камеры пересчёт матрицы
|
||||
calculateTransformationOperation.removeSequel(calculateMatrixOperation);
|
||||
// При изменении матрицы или FOV пересчёт плоскостей отсечения
|
||||
calculateMatrixOperation.removeSequel(calculatePlanesOperation);
|
||||
// При изменении плоскостей перерисовка
|
||||
calculatePlanesOperation.removeSequel(renderOperation);
|
||||
|
||||
if (_scene != null) {
|
||||
// Удаляем все операции из очереди
|
||||
_scene.removeOperation(calculateMatrixOperation);
|
||||
_scene.removeOperation(calculatePlanesOperation);
|
||||
_scene.removeOperation(renderOperation);
|
||||
// Отписываемся от сигналов сцены
|
||||
_scene.changePrimitivesOperation.removeSequel(renderOperation);
|
||||
}
|
||||
// Удаляем ссылку на вид
|
||||
_view = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override protected function defaultName():String {
|
||||
return "camera" + ++counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected override function createEmptyObject():Object3D {
|
||||
return new Camera3D();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected override function clonePropertiesFrom(source:Object3D):void {
|
||||
super.clonePropertiesFrom(source);
|
||||
|
||||
var src:Camera3D = Camera3D(source);
|
||||
orthographic = src._orthographic;
|
||||
zoom = src._zoom;
|
||||
fov = src._fov;
|
||||
}
|
||||
}
|
||||
}
|
||||
907
Alternativa3D5/5.0.0/alternativa/engine3d/core/Face.as
Normal file
907
Alternativa3D5/5.0.0/alternativa/engine3d/core/Face.as
Normal file
@@ -0,0 +1,907 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.types.Point3D;
|
||||
import alternativa.types.Set;
|
||||
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Point;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Грань, образованная тремя или более вершинами.
|
||||
*/
|
||||
final public class Face {
|
||||
// Операции
|
||||
/**
|
||||
* @private
|
||||
* Расчёт глобальной нормали плоскости грани.
|
||||
*/
|
||||
alternativa3d var calculateNormalOperation:Operation = new Operation("calculateNormal", this, calculateNormal, Operation.FACE_CALCULATE_NORMAL);
|
||||
/**
|
||||
* @private
|
||||
* Расчёт UV-координат (выполняется до трансформации, чтобы UV корректно разбились при построении BSP).
|
||||
*/
|
||||
alternativa3d var calculateUVOperation:Operation = new Operation("calculateUV", this, calculateUV, Operation.FACE_CALCULATE_UV);
|
||||
/**
|
||||
* @private
|
||||
* Обновление примитива в сцене.
|
||||
*/
|
||||
alternativa3d var updatePrimitiveOperation:Operation = new Operation("updatePrimitive", this, updatePrimitive, Operation.FACE_UPDATE_PRIMITIVE);
|
||||
/**
|
||||
* @private
|
||||
* Обновление материала.
|
||||
*/
|
||||
alternativa3d var updateMaterialOperation:Operation = new Operation("updateMaterial", this, updateMaterial, Operation.FACE_UPDATE_MATERIAL);
|
||||
/**
|
||||
* @private
|
||||
* Расчёт UV для фрагментов (выполняется после трансформации, если её не было).
|
||||
*/
|
||||
alternativa3d var calculateFragmentsUVOperation:Operation = new Operation("calculateFragmentsUV", this, calculateFragmentsUV, Operation.FACE_CALCULATE_FRAGMENTS_UV);
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Меш
|
||||
*/
|
||||
alternativa3d var _mesh:Mesh;
|
||||
/**
|
||||
* @private
|
||||
* Поверхность
|
||||
*/
|
||||
alternativa3d var _surface:Surface;
|
||||
/**
|
||||
* @private
|
||||
* Вершины грани
|
||||
*/
|
||||
alternativa3d var _vertices:Array;
|
||||
/**
|
||||
* @private
|
||||
* Количество вершин
|
||||
*/
|
||||
alternativa3d var _num:uint;
|
||||
/**
|
||||
* @private
|
||||
* Примитив
|
||||
*/
|
||||
alternativa3d var primitive:PolyPrimitive;
|
||||
|
||||
// UV-координаты
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _aUV:Point;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _bUV:Point;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var _cUV:Point;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Коэффициенты базовой UV-матрицы
|
||||
*/
|
||||
alternativa3d var uvMatrixBase:Matrix;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* UV Матрица перевода текстурных координат в изометрическую камеру.
|
||||
*/
|
||||
alternativa3d var uvMatrix:Matrix;
|
||||
/**
|
||||
* @private
|
||||
* Нормаль плоскости
|
||||
*/
|
||||
alternativa3d var globalNormal:Point3D = new Point3D();
|
||||
/**
|
||||
* @private
|
||||
* Смещение плоскости
|
||||
*/
|
||||
alternativa3d var globalOffset:Number;
|
||||
|
||||
/**
|
||||
* Создание экземпляра грани.
|
||||
*
|
||||
* @param vertices массив объектов типа <code>alternativa.engine3d.core.Vertex</code>, задающий вершины грани в
|
||||
* порядке обхода лицевой стороны грани против часовой стрелки.
|
||||
*
|
||||
* @see Vertex
|
||||
*/
|
||||
public function Face(vertices:Array) {
|
||||
// Сохраняем вершины
|
||||
_vertices = vertices;
|
||||
_num = vertices.length;
|
||||
|
||||
// Создаём оригинальный примитив
|
||||
primitive = PolyPrimitive.createPolyPrimitive();
|
||||
primitive.face = this;
|
||||
primitive.num = _num;
|
||||
|
||||
// Обрабатываем вершины
|
||||
for (var i:uint = 0; i < _num; i++) {
|
||||
var vertex:Vertex = vertices[i];
|
||||
// Добавляем координаты вершины в примитив
|
||||
primitive.points.push(vertex.globalCoords);
|
||||
// Добавляем пустые UV-координаты в примитив
|
||||
primitive.uvs.push(null);
|
||||
// Добавляем вершину в грань
|
||||
vertex.addToFace(this);
|
||||
}
|
||||
|
||||
// Расчёт нормали
|
||||
calculateNormalOperation.addSequel(updatePrimitiveOperation);
|
||||
|
||||
// Расчёт UV грани инициирует расчёт UV фрагментов
|
||||
calculateUVOperation.addSequel(calculateFragmentsUVOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчёт нормали в глобальных координатах
|
||||
*/
|
||||
private function calculateNormal():void {
|
||||
// Вектор AB
|
||||
var vertex:Vertex = _vertices[0];
|
||||
var av:Point3D = vertex.globalCoords;
|
||||
vertex = _vertices[1];
|
||||
var bv:Point3D = vertex.globalCoords;
|
||||
var abx:Number = bv.x - av.x;
|
||||
var aby:Number = bv.y - av.y;
|
||||
var abz:Number = bv.z - av.z;
|
||||
// Вектор AC
|
||||
vertex = _vertices[2];
|
||||
var cv:Point3D = vertex.globalCoords;
|
||||
var acx:Number = cv.x - av.x;
|
||||
var acy:Number = cv.y - av.y;
|
||||
var acz:Number = cv.z - av.z;
|
||||
// Перпендикуляр к плоскости
|
||||
globalNormal.x = acz*aby - acy*abz;
|
||||
globalNormal.y = acx*abz - acz*abx;
|
||||
globalNormal.z = acy*abx - acx*aby;
|
||||
// Нормализация перпендикуляра
|
||||
globalNormal.normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчитывает глобальное смещение плоскости грани.
|
||||
* Помечает конечные примитивы на удаление, а базовый на добавление в сцене.
|
||||
*/
|
||||
private function updatePrimitive():void {
|
||||
// Расчёт смещения
|
||||
var vertex:Vertex = _vertices[0];
|
||||
globalOffset = vertex.globalCoords.x*globalNormal.x + vertex.globalCoords.y*globalNormal.y + vertex.globalCoords.z*globalNormal.z;
|
||||
|
||||
removePrimitive(primitive);
|
||||
primitive.mobility = _mesh.inheritedMobility;
|
||||
_mesh._scene.addPrimitives.push(primitive);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Рекурсивно проходит по фрагментам примитива и отправляет конечные фрагменты на удаление из сцены
|
||||
*/
|
||||
private function removePrimitive(primitive:PolyPrimitive):void {
|
||||
if (primitive.fragment1 != null) {
|
||||
removePrimitive(primitive.fragment1);
|
||||
removePrimitive(primitive.fragment2);
|
||||
primitive.fragment1 = null;
|
||||
primitive.fragment2 = null;
|
||||
if (primitive != this.primitive) {
|
||||
primitive.parent = null;
|
||||
primitive.sibling = null;
|
||||
PolyPrimitive.destroyPolyPrimitive(primitive);
|
||||
}
|
||||
} else {
|
||||
// Если примитив в BSP-дереве
|
||||
if (primitive.node != null) {
|
||||
// Удаление примитива
|
||||
_mesh._scene.removeBSPPrimitive(primitive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Пометка на перерисовку фрагментов грани.
|
||||
*/
|
||||
private function updateMaterial():void {
|
||||
if (!updatePrimitiveOperation.queued) {
|
||||
changePrimitive(primitive);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Рекурсивно проходит по фрагментам примитива и отправляет конечные фрагменты на перерисовку
|
||||
*/
|
||||
private function changePrimitive(primitive:PolyPrimitive):void {
|
||||
if (primitive.fragment1 != null) {
|
||||
changePrimitive(primitive.fragment1);
|
||||
changePrimitive(primitive.fragment2);
|
||||
} else {
|
||||
_mesh._scene.changedPrimitives[primitive] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчёт UV-матрицы на основании первых трёх UV-координат.
|
||||
* Расчёт UV-координат для оставшихся точек.
|
||||
*/
|
||||
private function calculateUV():void {
|
||||
var i:uint;
|
||||
// Расчёт UV-матрицы
|
||||
if (_aUV != null && _bUV != null && _cUV != null) {
|
||||
var abu:Number = _bUV.x - _aUV.x;
|
||||
var abv:Number = _bUV.y - _aUV.y;
|
||||
var acu:Number = _cUV.x - _aUV.x;
|
||||
var acv:Number = _cUV.y - _aUV.y;
|
||||
var det:Number = abu*acv - abv*acu;
|
||||
if (uvMatrixBase == null) {
|
||||
uvMatrixBase = new Matrix();
|
||||
uvMatrix = new Matrix();
|
||||
}
|
||||
uvMatrixBase.a = acv/det;
|
||||
uvMatrixBase.b = -abv/det;
|
||||
uvMatrixBase.c = -acu/det;
|
||||
uvMatrixBase.d = abu/det;
|
||||
uvMatrixBase.tx = -(uvMatrixBase.a*_aUV.x + uvMatrixBase.c*_aUV.y);
|
||||
uvMatrixBase.ty = -(uvMatrixBase.b*_aUV.x + uvMatrixBase.d*_aUV.y);
|
||||
|
||||
// Заполняем UV в базовом примитиве
|
||||
primitive.uvs[0] = _aUV;
|
||||
primitive.uvs[1] = _bUV;
|
||||
primitive.uvs[2] = _cUV;
|
||||
|
||||
// Расчёт недостающих UV
|
||||
if (_num > 3) {
|
||||
var a:Point3D = primitive.points[0];
|
||||
var b:Point3D = primitive.points[1];
|
||||
var c:Point3D = primitive.points[2];
|
||||
|
||||
var ab1:Number;
|
||||
var ab2:Number;
|
||||
var ac1:Number;
|
||||
var ac2:Number;
|
||||
var ad1:Number;
|
||||
var ad2:Number;
|
||||
var abk:Number;
|
||||
var ack:Number;
|
||||
|
||||
var uv:Point;
|
||||
var point:Point3D;
|
||||
|
||||
// Выбор наиболее подходящих осей для расчёта
|
||||
if (((globalNormal.x < 0) ? -globalNormal.x : globalNormal.x) > ((globalNormal.y < 0) ? -globalNormal.y : globalNormal.y)) {
|
||||
if (((globalNormal.x < 0) ? -globalNormal.x : globalNormal.x) > ((globalNormal.z < 0) ? -globalNormal.z : globalNormal.z)) {
|
||||
// Ось X
|
||||
ab1 = b.y - a.y;
|
||||
ab2 = b.z - a.z;
|
||||
ac1 = c.y - a.y;
|
||||
ac2 = c.z - a.z;
|
||||
det = ab1*ac2 - ac1*ab2;
|
||||
for (i = 3; i < _num; i++) {
|
||||
point = primitive.points[i];
|
||||
ad1 = point.y - a.y;
|
||||
ad2 = point.z - a.z;
|
||||
abk = (ad1*ac2 - ac1*ad2)/det;
|
||||
ack = (ab1*ad2 - ad1*ab2)/det;
|
||||
uv = primitive.uvs[i];
|
||||
if (uv == null) {
|
||||
uv = new Point();
|
||||
primitive.uvs[i] = uv;
|
||||
}
|
||||
uv.x = _aUV.x + abu*abk + acu*ack;
|
||||
uv.y = _aUV.y + abv*abk + acv*ack;
|
||||
}
|
||||
} else {
|
||||
// Ось Z
|
||||
ab1 = b.x - a.x;
|
||||
ab2 = b.y - a.y;
|
||||
ac1 = c.x - a.x;
|
||||
ac2 = c.y - a.y;
|
||||
det = ab1*ac2 - ac1*ab2;
|
||||
for (i = 3; i < _num; i++) {
|
||||
point = primitive.points[i];
|
||||
ad1 = point.x - a.x;
|
||||
ad2 = point.y - a.y;
|
||||
abk = (ad1*ac2 - ac1*ad2)/det;
|
||||
ack = (ab1*ad2 - ad1*ab2)/det;
|
||||
uv = primitive.uvs[i];
|
||||
if (uv == null) {
|
||||
uv = new Point();
|
||||
primitive.uvs[i] = uv;
|
||||
}
|
||||
uv.x = _aUV.x + abu*abk + acu*ack;
|
||||
uv.y = _aUV.y + abv*abk + acv*ack;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (((globalNormal.y < 0) ? -globalNormal.y : globalNormal.y) > ((globalNormal.z < 0) ? -globalNormal.z : globalNormal.z)) {
|
||||
// Ось Y
|
||||
ab1 = b.x - a.x;
|
||||
ab2 = b.z - a.z;
|
||||
ac1 = c.x - a.x;
|
||||
ac2 = c.z - a.z;
|
||||
det = ab1*ac2 - ac1*ab2;
|
||||
for (i = 3; i < _num; i++) {
|
||||
point = primitive.points[i];
|
||||
ad1 = point.x - a.x;
|
||||
ad2 = point.z - a.z;
|
||||
abk = (ad1*ac2 - ac1*ad2)/det;
|
||||
ack = (ab1*ad2 - ad1*ab2)/det;
|
||||
uv = primitive.uvs[i];
|
||||
if (uv == null) {
|
||||
uv = new Point();
|
||||
primitive.uvs[i] = uv;
|
||||
}
|
||||
uv.x = _aUV.x + abu*abk + acu*ack;
|
||||
uv.y = _aUV.y + abv*abk + acv*ack;
|
||||
}
|
||||
} else {
|
||||
// Ось Z
|
||||
ab1 = b.x - a.x;
|
||||
ab2 = b.y - a.y;
|
||||
ac1 = c.x - a.x;
|
||||
ac2 = c.y - a.y;
|
||||
det = ab1*ac2 - ac1*ab2;
|
||||
for (i = 3; i < _num; i++) {
|
||||
point = primitive.points[i];
|
||||
ad1 = point.x - a.x;
|
||||
ad2 = point.y - a.y;
|
||||
abk = (ad1*ac2 - ac1*ad2)/det;
|
||||
ack = (ab1*ad2 - ad1*ab2)/det;
|
||||
uv = primitive.uvs[i];
|
||||
if (uv == null) {
|
||||
uv = new Point();
|
||||
primitive.uvs[i] = uv;
|
||||
}
|
||||
uv.x = _aUV.x + abu*abk + acu*ack;
|
||||
uv.y = _aUV.y + abv*abk + acv*ack;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Удаляем UV-матрицу
|
||||
uvMatrixBase = null;
|
||||
uvMatrix = null;
|
||||
// Удаляем UV-координаты из базового примитива
|
||||
for (i = 0; i < _num; i++) {
|
||||
primitive.uvs[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчёт UV-координат для фрагментов примитива, если не было трансформации
|
||||
*/
|
||||
private function calculateFragmentsUV():void {
|
||||
// Если в этом цикле не было трансформации
|
||||
if (!updatePrimitiveOperation.queued) {
|
||||
if (uvMatrixBase != null) {
|
||||
// Рассчитываем UV в примитиве
|
||||
calculatePrimitiveUV(primitive);
|
||||
} else {
|
||||
// Удаляем UV в примитиве
|
||||
removePrimitiveUV(primitive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчёт UV для точек базового примитива.
|
||||
*
|
||||
* @param primitive
|
||||
*/
|
||||
private function calculatePrimitiveUV(primitive:PolyPrimitive):void {
|
||||
if (primitive.fragment1 != null) {
|
||||
var points:Array = primitive.points;
|
||||
var points1:Array = primitive.fragment1.points;
|
||||
var points2:Array = primitive.fragment2.points;
|
||||
var uvs:Array = primitive.uvs;
|
||||
var uvs1:Array = primitive.fragment1.uvs;
|
||||
var uvs2:Array = primitive.fragment2.uvs;
|
||||
var index1:uint = 0;
|
||||
var index2:uint = 0;
|
||||
var point:Point3D;
|
||||
var uv:Point;
|
||||
var uv1:Point;
|
||||
var uv2:Point;
|
||||
var t:Number;
|
||||
var firstSplit:Boolean = true;
|
||||
for (var i:uint = 0; i < primitive.num; i++) {
|
||||
var split:Boolean = true;
|
||||
point = points[i];
|
||||
if (point == points2[index2]) {
|
||||
if (uvs2[index2] == null) {
|
||||
uvs2[index2] = uvs[i];
|
||||
}
|
||||
split = false;
|
||||
index2++;
|
||||
}
|
||||
if (point == points1[index1]) {
|
||||
if (uvs1[index1] == null) {
|
||||
uvs1[index1] = uvs[i];
|
||||
}
|
||||
split = false;
|
||||
index1++;
|
||||
}
|
||||
|
||||
if (split) {
|
||||
uv1 = uvs[(i == 0) ? (primitive.num - 1) : (i - 1)];
|
||||
uv2 = uvs[i];
|
||||
t = (firstSplit) ? primitive.splitTime1 : primitive.splitTime2;
|
||||
uv = uvs2[index2];
|
||||
if (uv == null) {
|
||||
uv = new Point(uv1.x + (uv2.x - uv1.x)*t, uv1.y + (uv2.y - uv1.y)*t);
|
||||
uvs2[index2] = uv;
|
||||
uvs1[index1] = uv;
|
||||
} else {
|
||||
uv.x = uv1.x + (uv2.x - uv1.x)*t;
|
||||
uv.y = uv1.y + (uv2.y - uv1.y)*t;
|
||||
}
|
||||
firstSplit = false;
|
||||
index2++;
|
||||
index1++;
|
||||
if (point == points2[index2]) {
|
||||
if (uvs2[index2] == null) {
|
||||
uvs2[index2] = uvs[i];
|
||||
}
|
||||
index2++;
|
||||
}
|
||||
if (point == points1[index1]) {
|
||||
if (uvs1[index1] == null) {
|
||||
uvs1[index1] = uvs[i];
|
||||
}
|
||||
index1++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Проверяем рассечение последнего ребра
|
||||
if (index2 < primitive.fragment2.num) {
|
||||
uv1 = uvs[primitive.num - 1];
|
||||
uv2 = uvs[0];
|
||||
t = (firstSplit) ? primitive.splitTime1 : primitive.splitTime2;
|
||||
uv = uvs2[index2];
|
||||
if (uv == null) {
|
||||
uv = new Point(uv1.x + (uv2.x - uv1.x)*t, uv1.y + (uv2.y - uv1.y)*t);
|
||||
uvs2[index2] = uv;
|
||||
uvs1[index1] = uv;
|
||||
} else {
|
||||
uv.x = uv1.x + (uv2.x - uv1.x)*t;
|
||||
uv.y = uv1.y + (uv2.y - uv1.y)*t;
|
||||
}
|
||||
}
|
||||
|
||||
calculatePrimitiveUV(primitive.fragment1);
|
||||
calculatePrimitiveUV(primitive.fragment2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление UV в примитиве и его фрагментах
|
||||
* @param primitive
|
||||
*/
|
||||
private function removePrimitiveUV(primitive:PolyPrimitive):void {
|
||||
// Очищаем список UV
|
||||
for (var i:uint = 0; i < primitive.num; i++) {
|
||||
primitive.uvs[i] = null;
|
||||
}
|
||||
// Если есть фрагменты, удаляем UV в них
|
||||
if (primitive.fragment1 != null) {
|
||||
removePrimitiveUV(primitive.fragment1);
|
||||
removePrimitiveUV(primitive.fragment2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Массив вершин грани, представленных объектами класса <code>alternativa.engine3d.core.Vertex</code>.
|
||||
*
|
||||
* @see Vertex
|
||||
*/
|
||||
public function get vertices():Array {
|
||||
return new Array().concat(_vertices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Количество вершин грани.
|
||||
*/
|
||||
public function get num():uint {
|
||||
return _num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mesh-объект, в котором находится грань.
|
||||
*/
|
||||
public function get mesh():Mesh {
|
||||
return _mesh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Поверхность, в которой находится грань.
|
||||
*/
|
||||
public function get surface():Surface {
|
||||
return _surface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Идентификатор грани в Mesh-объекте. В случае, если грань не содержится ни в одном Mesh-объекте, возвращается
|
||||
* <code>null</code>.
|
||||
*/
|
||||
public function get id():Object {
|
||||
return (_mesh != null) ? _mesh.getFaceId(this) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* UV-координаты, соответствующие первой вершине грани.
|
||||
*/
|
||||
public function get aUV():Point {
|
||||
return (_aUV != null) ? _aUV.clone() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* UV-координаты, соответствующие второй вершине грани.
|
||||
*/
|
||||
public function get bUV():Point {
|
||||
return (_bUV != null) ? _bUV.clone() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* UV-координаты, соответствующие третьей вершине грани.
|
||||
*/
|
||||
public function get cUV():Point {
|
||||
return (_cUV != null) ? _cUV.clone() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set aUV(value:Point):void {
|
||||
if (_aUV != null) {
|
||||
if (value != null) {
|
||||
if (!_aUV.equals(value)) {
|
||||
_aUV.x = value.x;
|
||||
_aUV.y = value.y;
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(calculateUVOperation);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_aUV = null;
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(calculateUVOperation);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (value != null) {
|
||||
_aUV = value.clone();
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(calculateUVOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set bUV(value:Point):void {
|
||||
if (_bUV != null) {
|
||||
if (value != null) {
|
||||
if (!_bUV.equals(value)) {
|
||||
_bUV.x = value.x;
|
||||
_bUV.y = value.y;
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(calculateUVOperation);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_bUV = null;
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(calculateUVOperation);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (value != null) {
|
||||
_bUV = value.clone();
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(calculateUVOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set cUV(value:Point):void {
|
||||
if (_cUV != null) {
|
||||
if (value != null) {
|
||||
if (!_cUV.equals(value)) {
|
||||
_cUV.x = value.x;
|
||||
_cUV.y = value.y;
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(calculateUVOperation);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_cUV = null;
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(calculateUVOperation);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (value != null) {
|
||||
_cUV = value.clone();
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(calculateUVOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Нормаль в локальной системе координат.
|
||||
*/
|
||||
public function get normal():Point3D {
|
||||
var res:Point3D = new Point3D();
|
||||
var vertex:Vertex = _vertices[0];
|
||||
var av:Point3D = vertex.coords;
|
||||
vertex = _vertices[1];
|
||||
var bv:Point3D = vertex.coords;
|
||||
var abx:Number = bv.x - av.x;
|
||||
var aby:Number = bv.y - av.y;
|
||||
var abz:Number = bv.z - av.z;
|
||||
vertex = _vertices[2];
|
||||
var cv:Point3D = vertex.coords;
|
||||
var acx:Number = cv.x - av.x;
|
||||
var acy:Number = cv.y - av.y;
|
||||
var acz:Number = cv.z - av.z;
|
||||
res.x = acz*aby - acy*abz;
|
||||
res.y = acx*abz - acz*abx;
|
||||
res.z = acy*abx - acx*aby;
|
||||
if (res.x != 0 || res.y != 0 || res.z != 0) {
|
||||
var k:Number = Math.sqrt(res.x*res.x + res.y*res.y + res.z*res.z);
|
||||
res.x /= k;
|
||||
res.y /= k;
|
||||
res.z /= k;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает UV-координаты для произвольной точки в системе координат полигональной сетки, которой принадлежит грань.
|
||||
*
|
||||
* @param point точка в плоскости грани, для которой производится расчёт UV-координат
|
||||
* @return UV-координаты заданной точки
|
||||
*/
|
||||
public function getUV(point:Point3D):Point {
|
||||
return getUVFast(point, normal);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчёт UV-координат для произвольной точки в локальной системе координат без расчёта
|
||||
* локальной нормали грани. Используется для оптимизации.
|
||||
*
|
||||
* @param point точка в плоскости грани, для которой производится расчёт UV-координат
|
||||
* @param normal нормаль плоскости грани в локальной системе координат
|
||||
* @return UV-координаты заданной точки
|
||||
*/
|
||||
alternativa3d function getUVFast(point:Point3D, normal:Point3D):Point {
|
||||
if (_aUV == null || _bUV == null || _cUV == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Выбор наиболее длинной оси нормали
|
||||
var dir:uint;
|
||||
if (((normal.x < 0) ? -normal.x : normal.x) > ((normal.y < 0) ? -normal.y : normal.y)) {
|
||||
if (((normal.x < 0) ? -normal.x : normal.x) > ((normal.z < 0) ? -normal.z : normal.z)) {
|
||||
dir = 0;
|
||||
} else {
|
||||
dir = 2;
|
||||
}
|
||||
} else {
|
||||
if (((normal.y < 0) ? -normal.y : normal.y) > ((normal.z < 0) ? -normal.z : normal.z)) {
|
||||
dir = 1;
|
||||
} else {
|
||||
dir = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Расчёт соотношения по векторам AB и AC
|
||||
var v:Vertex = _vertices[0];
|
||||
var a:Point3D = v._coords;
|
||||
v = _vertices[1];
|
||||
var b:Point3D = v._coords;
|
||||
v = _vertices[2];
|
||||
var c:Point3D = v._coords;
|
||||
|
||||
var ab1:Number = (dir == 0) ? (b.y - a.y) : (b.x - a.x);
|
||||
var ab2:Number = (dir == 2) ? (b.y - a.y) : (b.z - a.z);
|
||||
var ac1:Number = (dir == 0) ? (c.y - a.y) : (c.x - a.x);
|
||||
var ac2:Number = (dir == 2) ? (c.y - a.y) : (c.z - a.z);
|
||||
var det:Number = ab1*ac2 - ac1*ab2;
|
||||
|
||||
var ad1:Number = (dir == 0) ? (point.y - a.y) : (point.x - a.x);
|
||||
var ad2:Number = (dir == 2) ? (point.y - a.y) : (point.z - a.z);
|
||||
var abk:Number = (ad1*ac2 - ac1*ad2)/det;
|
||||
var ack:Number = (ab1*ad2 - ad1*ab2)/det;
|
||||
|
||||
// Интерполяция по UV первых точек
|
||||
var abu:Number = _bUV.x - _aUV.x;
|
||||
var abv:Number = _bUV.y - _aUV.y;
|
||||
var acu:Number = _cUV.x - _aUV.x;
|
||||
var acv:Number = _cUV.y - _aUV.y;
|
||||
|
||||
return new Point(_aUV.x + abu*abk + acu*ack, _aUV.y + abv*abk + acv*ack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Множество граней, имеющих общие рёбра с текущей гранью.
|
||||
*/
|
||||
public function get edgeJoinedFaces():Set {
|
||||
var res:Set = new Set(true);
|
||||
// Перебираем точки грани
|
||||
for (var i:uint = 0; i < _num; i++) {
|
||||
var a:Vertex = _vertices[i];
|
||||
var b:Vertex = _vertices[(i < _num - 1) ? (i + 1) : 0];
|
||||
|
||||
// Перебираем грани текущей точки
|
||||
for (var key:* in a._faces) {
|
||||
var face:Face = key;
|
||||
// Если это другая грань и у неё также есть следующая точка
|
||||
if (face != this && face._vertices.indexOf(b) >= 0) {
|
||||
// Значит у граней общее ребро
|
||||
res[face] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление всех вершин из грани.
|
||||
* Очистка базового примитива.
|
||||
*/
|
||||
alternativa3d function removeVertices():void {
|
||||
// Удалить вершины
|
||||
for (var i:uint = 0; i < _num; i++) {
|
||||
// Удаляем из списка
|
||||
var vertex:Vertex = _vertices.pop();
|
||||
// Удаляем координаты вершины из примитива
|
||||
primitive.points.pop();
|
||||
// Удаляем вершину из грани
|
||||
vertex.removeFromFace(this);
|
||||
}
|
||||
// Обнуляем количество вершин
|
||||
_num = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Добавление грани на сцену
|
||||
* @param scene
|
||||
*/
|
||||
alternativa3d function addToScene(scene:Scene3D):void {
|
||||
// При добавлении на сцену рассчитываем плоскость и UV
|
||||
scene.addOperation(calculateNormalOperation);
|
||||
scene.addOperation(calculateUVOperation);
|
||||
|
||||
// Подписываем сцену на операции
|
||||
updatePrimitiveOperation.addSequel(scene.calculateBSPOperation);
|
||||
updateMaterialOperation.addSequel(scene.changePrimitivesOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление грани из сцены
|
||||
* @param scene
|
||||
*/
|
||||
alternativa3d function removeFromScene(scene:Scene3D):void {
|
||||
// Удаляем все операции из очереди
|
||||
scene.removeOperation(calculateUVOperation);
|
||||
scene.removeOperation(calculateFragmentsUVOperation);
|
||||
scene.removeOperation(calculateNormalOperation);
|
||||
scene.removeOperation(updatePrimitiveOperation);
|
||||
scene.removeOperation(updateMaterialOperation);
|
||||
|
||||
// Удаляем примитивы из сцены
|
||||
removePrimitive(primitive);
|
||||
|
||||
// Посылаем операцию сцены на расчёт BSP
|
||||
scene.addOperation(scene.calculateBSPOperation);
|
||||
|
||||
// Отписываем сцену от операций
|
||||
updatePrimitiveOperation.removeSequel(scene.calculateBSPOperation);
|
||||
updateMaterialOperation.removeSequel(scene.changePrimitivesOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Добавление грани в меш
|
||||
* @param mesh
|
||||
*/
|
||||
alternativa3d function addToMesh(mesh:Mesh):void {
|
||||
// Подписка на операции меша
|
||||
mesh.changeCoordsOperation.addSequel(updatePrimitiveOperation);
|
||||
mesh.changeRotationOrScaleOperation.addSequel(calculateNormalOperation);
|
||||
mesh.calculateMobilityOperation.addSequel(updatePrimitiveOperation);
|
||||
// Сохранить меш
|
||||
_mesh = mesh;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление грани из меша
|
||||
* @param mesh
|
||||
*/
|
||||
alternativa3d function removeFromMesh(mesh:Mesh):void {
|
||||
// Отписка от операций меша
|
||||
mesh.changeCoordsOperation.removeSequel(updatePrimitiveOperation);
|
||||
mesh.changeRotationOrScaleOperation.removeSequel(calculateNormalOperation);
|
||||
mesh.calculateMobilityOperation.removeSequel(updatePrimitiveOperation);
|
||||
// Удалить ссылку на меш
|
||||
_mesh = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Добавление к поверхности
|
||||
*
|
||||
* @param surface
|
||||
*/
|
||||
alternativa3d function addToSurface(surface:Surface):void {
|
||||
// Подписка поверхности на операции
|
||||
surface.changeMaterialOperation.addSequel(updateMaterialOperation);
|
||||
// Если при смене поверхности изменился материал
|
||||
if (_mesh != null && (_surface != null && _surface._material != surface._material || _surface == null && surface._material != null)) {
|
||||
// Отправляем сигнал смены материала
|
||||
_mesh.addOperationToScene(updateMaterialOperation);
|
||||
}
|
||||
// Сохранить поверхность
|
||||
_surface = surface;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление из поверхности
|
||||
*
|
||||
* @param surface
|
||||
*/
|
||||
alternativa3d function removeFromSurface(surface:Surface):void {
|
||||
// Отписка поверхности от операций
|
||||
surface.changeMaterialOperation.removeSequel(updateMaterialOperation);
|
||||
// Если был материал
|
||||
if (surface._material != null) {
|
||||
// Отправляем сигнал смены материала
|
||||
_mesh.addOperationToScene(updateMaterialOperation);
|
||||
}
|
||||
// Удалить ссылку на поверхность
|
||||
_surface = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Строковое представление объекта.
|
||||
*
|
||||
* @return строковое представление объекта
|
||||
*/
|
||||
public function toString():String {
|
||||
var res:String = "[Face ID:" + id + ((_num > 0) ? " vertices:" : "");
|
||||
for (var i:uint = 0; i < _num; i++) {
|
||||
var vertex:Vertex = _vertices[i];
|
||||
res += vertex.id + ((i < _num - 1) ? ", " : "");
|
||||
}
|
||||
res += "]";
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
985
Alternativa3D5/5.0.0/alternativa/engine3d/core/Mesh.as
Normal file
985
Alternativa3D5/5.0.0/alternativa/engine3d/core/Mesh.as
Normal file
@@ -0,0 +1,985 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.engine3d.errors.FaceExistsError;
|
||||
import alternativa.engine3d.errors.FaceNeedMoreVerticesError;
|
||||
import alternativa.engine3d.errors.FaceNotFoundError;
|
||||
import alternativa.engine3d.errors.InvalidIDError;
|
||||
import alternativa.engine3d.errors.SurfaceExistsError;
|
||||
import alternativa.engine3d.errors.SurfaceNotFoundError;
|
||||
import alternativa.engine3d.errors.VertexExistsError;
|
||||
import alternativa.engine3d.errors.VertexNotFoundError;
|
||||
import alternativa.engine3d.materials.SurfaceMaterial;
|
||||
import alternativa.types.Map;
|
||||
import alternativa.utils.ObjectUtils;
|
||||
|
||||
import flash.geom.Point;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Полигональная сетка - базовый класс для трёхмерных объектов, состоящих из набора вершин и граней.
|
||||
*/
|
||||
public class Mesh extends Object3D {
|
||||
|
||||
// Инкремент количества объектов
|
||||
private static var counter:uint = 0;
|
||||
|
||||
// Инкременты для идентификаторов вершин, граней и поверхностей
|
||||
private var vertexIdCounter:uint = 0;
|
||||
private var faceIdCounter:uint = 0;
|
||||
private var surfaceIdCounter:uint = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Список вершин
|
||||
*/
|
||||
alternativa3d var _vertices:Map = new Map();
|
||||
/**
|
||||
* @private
|
||||
* Список граней
|
||||
*/
|
||||
alternativa3d var _faces:Map = new Map();
|
||||
/**
|
||||
* @private
|
||||
* Список поверхностей
|
||||
*/
|
||||
alternativa3d var _surfaces:Map = new Map();
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param name имя экземпляра
|
||||
*/
|
||||
public function Mesh(name:String = null) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавление новой вершины.
|
||||
*
|
||||
* @param x координата X в локальной системе координат
|
||||
* @param y координата Y в локальной системе координат
|
||||
* @param z координата Z в локальной системе координат
|
||||
* @param id идентификатор вершины. В случае если указано значение <code>null</code>, идентификатор будет
|
||||
* сформирован автоматически.
|
||||
*
|
||||
* @return добавленный экземпляр вершины
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.VertexExistsError объект уже содержит вершину с указанным идентификатором
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора
|
||||
*/
|
||||
public function addVertex(x:Number = 0, y:Number = 0, z:Number = 0, id:Object = null):Vertex {
|
||||
// Проверяем ID
|
||||
if (id != null) {
|
||||
// Если уже есть вершина с таким ID
|
||||
if (_vertices[id] != undefined) {
|
||||
if (_vertices[id] is Vertex) {
|
||||
throw new VertexExistsError(id, this);
|
||||
} else {
|
||||
throw new InvalidIDError(id, this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Ищем первый свободный
|
||||
while (_vertices[vertexIdCounter] != undefined) {
|
||||
vertexIdCounter++;
|
||||
}
|
||||
id = vertexIdCounter;
|
||||
}
|
||||
|
||||
// Создаём вершину
|
||||
var v:Vertex = new Vertex(x, y, z);
|
||||
|
||||
// Добавляем вершину на сцену
|
||||
if (_scene != null) {
|
||||
v.addToScene(_scene);
|
||||
}
|
||||
|
||||
// Добавляем вершину в меш
|
||||
v.addToMesh(this);
|
||||
_vertices[id] = v;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаление вершины. При удалении вершины из объекта также удаляются все грани, в которых содержится данная вершина.
|
||||
*
|
||||
* @param vertex экземпляр класса <code>alternativa.engine3d.core.Vertex</code> или идентификатор удаляемой вершины
|
||||
*
|
||||
* @return удалённый экземпляр вершины
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.VertexNotFoundError объект не содержит указанную вершину
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимый идентификатор вершины
|
||||
*/
|
||||
public function removeVertex(vertex:Object):Vertex {
|
||||
var byLink:Boolean = vertex is Vertex;
|
||||
|
||||
// Проверяем на null
|
||||
if (vertex == null) {
|
||||
throw new VertexNotFoundError(null, this);
|
||||
}
|
||||
|
||||
// Проверяем наличие вершины в меше
|
||||
if (byLink) {
|
||||
// Если удаляем по ссылке
|
||||
if (Vertex(vertex)._mesh != this) {
|
||||
// Если вершина не в меше
|
||||
throw new VertexNotFoundError(vertex, this);
|
||||
}
|
||||
} else {
|
||||
// Если удаляем по ID
|
||||
if (_vertices[vertex] == undefined) {
|
||||
// Если нет вершины с таким ID
|
||||
throw new VertexNotFoundError(vertex, this);
|
||||
} else if (!(_vertices[vertex] is Vertex)) {
|
||||
// По этому id не вершина
|
||||
throw new InvalidIDError(vertex, this);
|
||||
}
|
||||
}
|
||||
|
||||
// Находим вершину и её ID
|
||||
var v:Vertex = byLink ? Vertex(vertex) : _vertices[vertex];
|
||||
var id:Object = byLink ? getVertexId(Vertex(vertex)) : vertex;
|
||||
|
||||
// Удаляем вершину из сцены
|
||||
if (_scene != null) {
|
||||
v.removeFromScene(_scene);
|
||||
}
|
||||
|
||||
// Удаляем вершину из меша
|
||||
v.removeFromMesh(this);
|
||||
delete _vertices[id];
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавление новой грани.
|
||||
*
|
||||
* @param vertices массив вершин грани, указанных в порядке обхода лицевой стороны грани против часовой
|
||||
* стрелки. Каждый элемент массива может быть либо экземпляром класса <code>alternativa.engine3d.core.Vertex</code>,
|
||||
* либо идентификатором в наборе вершин объекта. В обоих случаях объект должен содержать указанную вершину.
|
||||
* @param id идентификатор грани. В случае если указано значение <code>null</code>, идентификатор будет
|
||||
* сформирован автоматически.
|
||||
*
|
||||
* @return экземпляр добавленной грани
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.FaceNeedMoreVerticesError в качестве массива вершин был передан
|
||||
* <code>null</code>, либо количество вершин в массиве меньше трех
|
||||
* @throws alternativa.engine3d.errors.FaceExistsError объект уже содержит грань с заданным идентификатором
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора для грани
|
||||
* @throws alternativa.engine3d.errors.VertexNotFoundError объект не содержит какую-либо вершину из входного массива
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора для вершины
|
||||
*
|
||||
* @see Vertex
|
||||
*/
|
||||
public function addFace(vertices:Array, id:Object = null):Face {
|
||||
|
||||
// Проверяем на null
|
||||
if (vertices == null) {
|
||||
throw new FaceNeedMoreVerticesError(this);
|
||||
}
|
||||
|
||||
// Проверяем ID
|
||||
if (id != null) {
|
||||
// Если уже есть грань с таким ID
|
||||
if (_faces[id] != undefined) {
|
||||
if (_faces[id] is Face) {
|
||||
throw new FaceExistsError(id, this);
|
||||
} else {
|
||||
throw new InvalidIDError(id, this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Ищем первый свободный ID
|
||||
while (_faces[faceIdCounter] != undefined) {
|
||||
faceIdCounter++;
|
||||
}
|
||||
id = faceIdCounter;
|
||||
}
|
||||
|
||||
// Проверяем количество точек
|
||||
var length:uint = vertices.length;
|
||||
if (length < 3) {
|
||||
throw new FaceNeedMoreVerticesError(this, length);
|
||||
}
|
||||
|
||||
// Проверяем и формируем список вершин
|
||||
var v:Array = new Array();
|
||||
var vertex:Vertex;
|
||||
for (var i:uint = 0; i < length; i++) {
|
||||
if (vertices[i] is Vertex) {
|
||||
// Если работаем со ссылками
|
||||
vertex = vertices[i];
|
||||
if (vertex._mesh != this) {
|
||||
// Если вершина не в меше
|
||||
throw new VertexNotFoundError(vertices[i], this);
|
||||
}
|
||||
} else {
|
||||
// Если работаем с ID
|
||||
if (_vertices[vertices[i]] == null) {
|
||||
// Если нет вершины с таким ID
|
||||
throw new VertexNotFoundError(vertices[i], this);
|
||||
} else if (!(_vertices[vertices[i]] is Vertex)) {
|
||||
// Если id зарезервировано
|
||||
throw new InvalidIDError(vertices[i],this);
|
||||
}
|
||||
vertex = _vertices[vertices[i]];
|
||||
}
|
||||
v.push(vertex);
|
||||
}
|
||||
|
||||
// Создаём грань
|
||||
var f:Face = new Face(v);
|
||||
|
||||
// Добавляем грань на сцену
|
||||
if (_scene != null) {
|
||||
f.addToScene(_scene);
|
||||
}
|
||||
|
||||
// Добавляем грань в меш
|
||||
f.addToMesh(this);
|
||||
_faces[id] = f;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаление грани.
|
||||
*
|
||||
* @param экземпляр класса <code>alternativa.engine3d.core.Face</code> или идентификатор удаляемой грани
|
||||
*
|
||||
* @return экземпляр удаленной грани
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.FaceNotFoundError объект не содержит указанную грань
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора грани
|
||||
*/
|
||||
public function removeFace(face:Object):Face {
|
||||
var byLink:Boolean = face is Face;
|
||||
|
||||
// Проверяем на null
|
||||
if (face == null) {
|
||||
throw new FaceNotFoundError(null, this);
|
||||
}
|
||||
|
||||
// Проверяем наличие грани в меше
|
||||
if (byLink) {
|
||||
// Если удаляем по ссылке
|
||||
if (Face(face)._mesh != this) {
|
||||
// Если грань не в меше
|
||||
throw new FaceNotFoundError(face, this);
|
||||
}
|
||||
} else {
|
||||
// Если удаляем по ID
|
||||
if (_faces[face] == undefined) {
|
||||
// Если нет грани с таким ID
|
||||
throw new FaceNotFoundError(face, this);
|
||||
} else if (!(_faces[face] is Face)) {
|
||||
throw new InvalidIDError(face, this);
|
||||
}
|
||||
}
|
||||
|
||||
// Находим грань и её ID
|
||||
var f:Face = byLink ? Face(face) : _faces[face] ;
|
||||
var id:Object = byLink ? getFaceId(Face(face)) : face;
|
||||
|
||||
// Удаляем грань из сцены
|
||||
if (_scene != null) {
|
||||
f.removeFromScene(_scene);
|
||||
}
|
||||
|
||||
// Удаляем вершины из грани
|
||||
f.removeVertices();
|
||||
|
||||
// Удаляем грань из поверхности
|
||||
if (f._surface != null) {
|
||||
f._surface._faces.remove(f);
|
||||
f.removeFromSurface(f._surface);
|
||||
}
|
||||
|
||||
// Удаляем грань из меша
|
||||
f.removeFromMesh(this);
|
||||
delete _faces[id];
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавление новой поверхности.
|
||||
*
|
||||
* @param faces набор граней, составляющих поверхность. Каждый элемент массива должен быть либо экземпляром класса
|
||||
* <code>alternativa.engine3d.core.Face</code>, либо идентификатором грани. В обоих случаях объект должен содержать
|
||||
* указанную грань. Если значение параметра равно <code>null</code>, то будет создана пустая поверхность. Если
|
||||
* какая-либо грань содержится в другой поверхности, она будет перенесена в новую поверхность.
|
||||
* @param id идентификатор новой поверхности. В случае если указано значение <code>null</code>, идентификатор будет
|
||||
* сформирован автоматически.
|
||||
*
|
||||
* @return экземпляр добавленной поверхности
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.SurfaceExistsError объект уже содержит поверхность с заданным идентификатором
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора поверхности
|
||||
*
|
||||
* @see Face
|
||||
*/
|
||||
public function addSurface(faces:Array = null, id:Object = null):Surface {
|
||||
// Проверяем ID
|
||||
if (id != null) {
|
||||
// Если уже есть поверхность с таким ID
|
||||
if (_surfaces[id] != undefined) {
|
||||
if (_surfaces[id] is Surface) {
|
||||
throw new SurfaceExistsError(id, this);
|
||||
} else {
|
||||
throw new InvalidIDError(id, this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Ищем первый свободный ID
|
||||
while (_surfaces[surfaceIdCounter] != undefined) {
|
||||
surfaceIdCounter++;
|
||||
}
|
||||
id = surfaceIdCounter;
|
||||
}
|
||||
|
||||
// Создаём поверхность
|
||||
var s:Surface = new Surface();
|
||||
|
||||
// Добавляем поверхность на сцену
|
||||
if (_scene != null) {
|
||||
s.addToScene(_scene);
|
||||
}
|
||||
|
||||
// Добавляем поверхность в меш
|
||||
s.addToMesh(this);
|
||||
_surfaces[id] = s;
|
||||
|
||||
// Добавляем грани, если есть
|
||||
if (faces != null) {
|
||||
var length:uint = faces.length;
|
||||
for (var i:uint = 0; i < length; i++) {
|
||||
s.addFace(faces[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаление поверхности.
|
||||
*
|
||||
* @param surface экземпляр класса <code>alternativa.engine3d.core.Face</code> или идентификатор удаляемой поверхности
|
||||
*
|
||||
* @return экземпляр удаленной поверхности
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит указанную поверхность
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора поверхности
|
||||
*/
|
||||
public function removeSurface(surface:Object):Surface {
|
||||
var byLink:Boolean = surface is Surface;
|
||||
|
||||
// Проверяем на null
|
||||
if (surface == null) {
|
||||
throw new SurfaceNotFoundError(null, this);
|
||||
}
|
||||
|
||||
// Проверяем наличие поверхности в меше
|
||||
if (byLink) {
|
||||
// Если удаляем по ссылке
|
||||
if (Surface(surface)._mesh != this) {
|
||||
// Если поверхность не в меше
|
||||
throw new SurfaceNotFoundError(surface, this);
|
||||
}
|
||||
} else {
|
||||
// Если удаляем по ID
|
||||
if (_surfaces[surface] == undefined) {
|
||||
// Если нет поверхности с таким ID
|
||||
throw new SurfaceNotFoundError(surface, this);
|
||||
} else if (!(_surfaces[surface] is Surface)) {
|
||||
throw new InvalidIDError(surface, this);
|
||||
}
|
||||
}
|
||||
|
||||
// Находим поверхность и её ID
|
||||
var s:Surface = byLink ? Surface(surface) : _surfaces[surface];
|
||||
var id:Object = byLink ? getSurfaceId(Surface(surface)) : surface;
|
||||
|
||||
// Удаляем поверхность из сцены
|
||||
if (_scene != null) {
|
||||
s.removeFromScene(_scene);
|
||||
}
|
||||
|
||||
// Удаляем грани из поверхности
|
||||
s.removeFaces();
|
||||
|
||||
// Удаляем поверхность из меша
|
||||
s.removeFromMesh(this);
|
||||
delete _surfaces[id];
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавление всех граней полигональной сетки в указанную поверхность.
|
||||
*
|
||||
* @param surface экземпляр класса <code>alternativa.engine3d.core.Surface</code> или идентификатор поверхности, в
|
||||
* которую добавляются грани. Если задан идентификатор, и объект не содержит поверхность с таким идентификатором,
|
||||
* будет создана новая поверхность.
|
||||
*
|
||||
* @param removeSurfaces удалять или нет пустые поверхности после переноса граней
|
||||
*
|
||||
* @return экземпляр поверхности, в которую перенесены грани
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит указанный экземпляр поверхности
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора поверхности
|
||||
*/
|
||||
public function addAllFacesToSurface(surface:Object = null, removeSurfaces:Boolean = false):Surface {
|
||||
var returnSurface:Surface;
|
||||
var returnSurfaceId:Object;
|
||||
if (surface is Surface) {
|
||||
// Работаем с экземпляром Surface
|
||||
if (surface._mesh == this) {
|
||||
returnSurface = Surface(surface);
|
||||
} else {
|
||||
throw new SurfaceNotFoundError(surface, this);
|
||||
}
|
||||
} else {
|
||||
// Работаем с идентификатором
|
||||
if (_surfaces[surface] == undefined) {
|
||||
// Поверхности еще нет
|
||||
returnSurface = addSurface(null, surface);
|
||||
returnSurfaceId = surface;
|
||||
} else {
|
||||
if (_surfaces[surface] is Surface) {
|
||||
returnSurface = _surfaces[surface];
|
||||
} else {
|
||||
// _surfaces[surface] по идентификатору возвращает не Surface
|
||||
throw new InvalidIDError(surface, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Перемещаем все грани
|
||||
for each (var face:Face in _faces) {
|
||||
if (face._surface != returnSurface) {
|
||||
returnSurface.addFace(face);
|
||||
}
|
||||
}
|
||||
if (removeSurfaces) {
|
||||
// Удаляем старые, теперь вручную - меньше проверок, но рискованно
|
||||
if (returnSurfaceId == null) {
|
||||
returnSurfaceId = getSurfaceId(returnSurface);
|
||||
}
|
||||
var newSurfaces:Map = new Map();
|
||||
newSurfaces[returnSurfaceId] = returnSurface;
|
||||
delete _surfaces[returnSurfaceId];
|
||||
// Удаляем оставшиеся
|
||||
for (var currentSurfaceId:* in _surfaces) {
|
||||
// Удаляем поверхность из сцены
|
||||
var currentSurface:Surface = _surfaces[currentSurfaceId];
|
||||
if (_scene != null) {
|
||||
currentSurface.removeFromScene(_scene);
|
||||
}
|
||||
// Удаляем поверхность из меша
|
||||
currentSurface.removeFromMesh(this);
|
||||
delete _surfaces[currentSurfaceId];
|
||||
}
|
||||
// Новый список граней
|
||||
_surfaces = newSurfaces;
|
||||
}
|
||||
return returnSurface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Установка материала для указанной поверхности.
|
||||
*
|
||||
* @param material материал, назначаемый поверхности. Один экземпляр SurfaceMaterial можно назначить только одной поверхности.
|
||||
* @param surface экземпляр класса <code>alternativa.engine3d.core.Surface</code> или идентификатор поверхности
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит указанную поверхность
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора поверхности
|
||||
*
|
||||
* @see Surface
|
||||
*/
|
||||
public function setMaterialToSurface(material:SurfaceMaterial, surface:Object):void {
|
||||
var byLink:Boolean = surface is Surface;
|
||||
|
||||
// Проверяем на null
|
||||
if (surface == null) {
|
||||
throw new SurfaceNotFoundError(null, this);
|
||||
}
|
||||
|
||||
// Проверяем наличие поверхности в меше
|
||||
if (byLink) {
|
||||
// Если назначаем по ссылке
|
||||
if (Surface(surface)._mesh != this) {
|
||||
// Если поверхность не в меше
|
||||
throw new SurfaceNotFoundError(surface, this);
|
||||
}
|
||||
} else {
|
||||
// Если назначаем по ID
|
||||
if (_surfaces[surface] == undefined) {
|
||||
// Если нет поверхности с таким ID
|
||||
throw new SurfaceNotFoundError(surface, this);
|
||||
} else if (!(_surfaces[surface] is Surface)) {
|
||||
throw new InvalidIDError(surface, this);
|
||||
}
|
||||
}
|
||||
|
||||
// Находим поверхность
|
||||
var s:Surface = byLink ? Surface(surface) : _surfaces[surface];
|
||||
|
||||
// Назначаем материал
|
||||
s.material = material;
|
||||
}
|
||||
|
||||
/**
|
||||
* Установка материала для всех поверхностей объекта. Первой поверхности назначается указанный материал, всем остальным
|
||||
* назначаются его клоны.
|
||||
*
|
||||
* @param material устанавливаемый материал
|
||||
*/
|
||||
public function setMaterialToAllSurfaces(material:SurfaceMaterial):void {
|
||||
var firstSurface:Boolean = true;
|
||||
for each (var surface:Surface in _surfaces) {
|
||||
if (firstSurface) {
|
||||
firstSurface = false;
|
||||
surface.material = material;
|
||||
} else {
|
||||
surface.material = (material != null) ? SurfaceMaterial(material.clone()) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Установка UV-координат для указанной грани. Матрица преобразования UV-координат расчитывается по UV-координатам
|
||||
* первых трёх вершин грани.
|
||||
*
|
||||
* @param aUV UV-координаты, соответствующие первой вершине
|
||||
* @param bUV UV-координаты, соответствующие второй вершине
|
||||
* @param cUV UV-координаты, соответствующие третьей вершине
|
||||
* @param face экземпляр класса <code>alternativa.engine3d.core.Face</code> или идентификатор грани
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.FaceNotFoundError объект не содержит указанную грань
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора грани
|
||||
*
|
||||
* @see Face
|
||||
*/
|
||||
public function setUVsToFace(aUV:Point, bUV:Point, cUV:Point, face:Object):void {
|
||||
var byLink:Boolean = face is Face;
|
||||
|
||||
// Проверяем на null
|
||||
if (face == null) {
|
||||
throw new FaceNotFoundError(null, this);
|
||||
}
|
||||
|
||||
// Проверяем наличие грани в меше
|
||||
if (byLink) {
|
||||
// Если назначаем по ссылке
|
||||
if (Face(face)._mesh != this) {
|
||||
// Если грань не в меше
|
||||
throw new FaceNotFoundError(face, this);
|
||||
}
|
||||
} else {
|
||||
// Если назначаем по ID
|
||||
if (_faces[face] == undefined) {
|
||||
// Если нет грани с таким ID
|
||||
throw new FaceNotFoundError(face, this);
|
||||
} else if (!(_faces[face] is Face)) {
|
||||
throw new InvalidIDError(face, this);
|
||||
}
|
||||
}
|
||||
|
||||
// Находим грань
|
||||
var f:Face = byLink ? Face(face) : _faces[face];
|
||||
|
||||
// Назначаем UV-координаты
|
||||
f.aUV = aUV;
|
||||
f.bUV = bUV;
|
||||
f.cUV = cUV;
|
||||
}
|
||||
|
||||
/**
|
||||
* Набор вершин объекта. Ключами ассоциативного массива являются идентификаторы вершин, а значения вершинами.
|
||||
*/
|
||||
public function get vertices():Map {
|
||||
return _vertices.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Набор граней объекта. Ключами ассоциативного массива являются идентификаторы граней, а значения гранями.
|
||||
*/
|
||||
public function get faces():Map {
|
||||
return _faces.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Набор поверхностей объекта. Ключами ассоциативного массива являются идентификаторы поверхностей, а значения поверхностями.
|
||||
*/
|
||||
public function get surfaces():Map {
|
||||
return _surfaces.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение вершины по её идентификатору.
|
||||
*
|
||||
* @param id идентификатор вершины
|
||||
*
|
||||
* @return экземпляр вершины с идентификатором id
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.VertexNotFoundError объект не содержит вершину с указанным идентификатором
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора вершины
|
||||
*/
|
||||
public function getVertexById(id:Object):Vertex {
|
||||
if (id == null) {
|
||||
throw new VertexNotFoundError(null, this);
|
||||
}
|
||||
if (_vertices[id] == undefined) {
|
||||
// Если нет вершины с таким ID
|
||||
throw new VertexNotFoundError(id, this);
|
||||
} else {
|
||||
if (_vertices[id] is Vertex) {
|
||||
return _vertices[id];
|
||||
} else {
|
||||
throw new InvalidIDError(id, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение идентификатора вершины.
|
||||
*
|
||||
* @param экземпляр вершины
|
||||
*
|
||||
* @return идентификатор вершины
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.VertexNotFoundError объект не содержит указанную вершину
|
||||
*/
|
||||
public function getVertexId(vertex:Vertex):Object {
|
||||
if (vertex == null) {
|
||||
throw new VertexNotFoundError(null, this);
|
||||
}
|
||||
if (vertex._mesh != this) {
|
||||
// Если вершина не в меше
|
||||
throw new VertexNotFoundError(vertex, this);
|
||||
}
|
||||
for (var i:Object in _vertices) {
|
||||
if (_vertices[i] == vertex) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new VertexNotFoundError(vertex, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка наличия вершины в объекте.
|
||||
*
|
||||
* @param vertex экземпляр класса <code>alternativa.engine3d.core.Vertex</code> или идентификатор вершины
|
||||
*
|
||||
* @return <code>true</code>, если объект содержит указанную вершину, <code>false</code> в противном случае
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.VertexNotFoundError в качестве vertex был передан null
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора вершины
|
||||
*
|
||||
* @see Vertex
|
||||
*/
|
||||
public function hasVertex(vertex:Object):Boolean {
|
||||
if (vertex == null) {
|
||||
throw new VertexNotFoundError(null, this);
|
||||
}
|
||||
if (vertex is Vertex) {
|
||||
// Проверка вершины
|
||||
return vertex._mesh == this;
|
||||
} else {
|
||||
// Проверка ID вершины
|
||||
if (_vertices[vertex] != undefined) {
|
||||
// По этому ID есть объект
|
||||
if (_vertices[vertex] is Vertex) {
|
||||
// Объект является вершиной
|
||||
return true;
|
||||
} else {
|
||||
// ID некорректный
|
||||
throw new InvalidIDError(vertex, this);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение грани по ее идентификатору
|
||||
*
|
||||
* @param id идентификатор грани
|
||||
*
|
||||
* @return грань с идентификатором id
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.FaceNotFoundError объект не содержит грань с идентификатором id
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора грани
|
||||
*/
|
||||
public function getFaceById(id:Object):Face {
|
||||
if (id == null) {
|
||||
throw new FaceNotFoundError(null, this);
|
||||
}
|
||||
if (_faces[id] == undefined) {
|
||||
// Если нет грани с таким ID
|
||||
throw new FaceNotFoundError(id, this);
|
||||
} else {
|
||||
if (_faces[id] is Face) {
|
||||
return _faces[id];
|
||||
} else {
|
||||
throw new InvalidIDError(id, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение идентификатора грани.
|
||||
*
|
||||
* @param face экземпляр грани
|
||||
*
|
||||
* @return идентификатор указанной грани
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.FaceNotFoundError объект не содержит указанную грань
|
||||
*/
|
||||
public function getFaceId(face:Face):Object {
|
||||
if (face == null) {
|
||||
throw new FaceNotFoundError(null, this);
|
||||
}
|
||||
if (face._mesh != this) {
|
||||
// Если грань не в меше
|
||||
throw new FaceNotFoundError(face, this);
|
||||
}
|
||||
for (var i:Object in _faces) {
|
||||
if (_faces[i] == face) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new FaceNotFoundError(face, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка наличия грани в объекте.
|
||||
*
|
||||
* @param face экземпляр класса <code>Face</code> или идентификатор грани
|
||||
*
|
||||
* @return <code>true</code>, если объект содержит указанную грань, <code>false</code> в противном случае
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.FaceNotFoundError в качестве face был указан null
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора грани
|
||||
*/
|
||||
public function hasFace(face:Object):Boolean {
|
||||
if (face == null) {
|
||||
throw new FaceNotFoundError(null, this);
|
||||
}
|
||||
if (face is Face) {
|
||||
// Проверка грани
|
||||
return face._mesh == this;
|
||||
} else {
|
||||
// Проверка ID грани
|
||||
if (_faces[face] != undefined) {
|
||||
// По этому ID есть объект
|
||||
if (_faces[face] is Face) {
|
||||
// Объект является гранью
|
||||
return true;
|
||||
} else {
|
||||
// ID некорректный
|
||||
throw new InvalidIDError(face, this);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение поверхности по ее идентификатору
|
||||
*
|
||||
* @param id идентификатор поверхности
|
||||
*
|
||||
* @return экземпляр поверхности с идентификатором id
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит поверхность с идентификатором id
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора поверхности
|
||||
*/
|
||||
public function getSurfaceById(id:Object):Surface {
|
||||
if (id == null) {
|
||||
throw new SurfaceNotFoundError(null, this);
|
||||
}
|
||||
if (_surfaces[id] == undefined) {
|
||||
// Если нет поверхности с таким ID
|
||||
throw new SurfaceNotFoundError(id, this);
|
||||
} else {
|
||||
if (_surfaces[id] is Surface) {
|
||||
return _surfaces[id];
|
||||
} else {
|
||||
throw new InvalidIDError(id, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение идентификатора поверхности.
|
||||
*
|
||||
* @param surface экземпляр поверхности
|
||||
*
|
||||
* @return идентификатор поверхности
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит указанную поверхность
|
||||
*/
|
||||
public function getSurfaceId(surface:Surface):Object {
|
||||
if (surface == null) {
|
||||
throw new SurfaceNotFoundError(null, this);
|
||||
}
|
||||
if (surface._mesh != this) {
|
||||
// Если поверхность не в меше
|
||||
throw new SurfaceNotFoundError(surface, this);
|
||||
}
|
||||
for (var i:Object in _surfaces) {
|
||||
if (_surfaces[i] == surface) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка наличия поверхности в объекте.
|
||||
*
|
||||
* @param surface экземпляр класса <code>Surface</code> или идентификатор поверхности
|
||||
*
|
||||
* @return <code>true</true>, если объект содержит указанную поверхность, <code>false</code> в противном случае
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.SurfaceNotFoundError в качестве surface был передан null
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение идентификатора поверхности
|
||||
*/
|
||||
public function hasSurface(surface:Object):Boolean {
|
||||
if (surface == null) {
|
||||
throw new SurfaceNotFoundError(null, this);
|
||||
}
|
||||
if (surface is Surface) {
|
||||
// Проверка поверхности
|
||||
return surface._mesh == this;
|
||||
} else {
|
||||
// Проверка ID поверхности
|
||||
if (_surfaces[surface] != undefined) {
|
||||
// По этому ID есть объект
|
||||
if (_surfaces[surface] is Surface) {
|
||||
// Объект является поверхностью
|
||||
return true;
|
||||
} else {
|
||||
// ID некорректный
|
||||
throw new InvalidIDError(surface, this);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @inheritDoc
|
||||
*/
|
||||
override alternativa3d function setScene(value:Scene3D):void {
|
||||
if (_scene != value) {
|
||||
var vertex:Vertex;
|
||||
var face:Face;
|
||||
var surface:Surface;
|
||||
if (value != null) {
|
||||
// Добавить вершины на сцену
|
||||
for each (vertex in _vertices) {
|
||||
vertex.addToScene(value);
|
||||
}
|
||||
// Добавить грани на сцену
|
||||
for each (face in _faces) {
|
||||
face.addToScene(value);
|
||||
}
|
||||
// Добавить поверхности на сцену
|
||||
for each (surface in _surfaces) {
|
||||
surface.addToScene(value);
|
||||
}
|
||||
} else {
|
||||
// Удалить вершины из сцены
|
||||
for each (vertex in _vertices) {
|
||||
vertex.removeFromScene(_scene);
|
||||
}
|
||||
// Удалить грани из сцены
|
||||
for each (face in _faces) {
|
||||
face.removeFromScene(_scene);
|
||||
}
|
||||
// Удалить поверхности из сцены
|
||||
for each (surface in _surfaces) {
|
||||
surface.removeFromScene(_scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
super.setScene(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override protected function defaultName():String {
|
||||
return "mesh" + ++counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override public function toString():String {
|
||||
return "[" + ObjectUtils.getClassName(this) + " " + _name + " vertices: " + _vertices.length + " faces: " + _faces.length + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected override function createEmptyObject():Object3D {
|
||||
return new Mesh();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected override function clonePropertiesFrom(source:Object3D):void {
|
||||
super.clonePropertiesFrom(source);
|
||||
|
||||
var src:Mesh = Mesh(source);
|
||||
|
||||
var id:*;
|
||||
var len:int;
|
||||
var i:int;
|
||||
// Копирование вершин
|
||||
var vertexMap:Map = new Map(true);
|
||||
for (id in src._vertices) {
|
||||
var sourceVertex:Vertex = src._vertices[id];
|
||||
vertexMap[sourceVertex] = addVertex(sourceVertex.x, sourceVertex.y, sourceVertex.z, id);
|
||||
}
|
||||
|
||||
// Копирование граней
|
||||
var faceMap:Map = new Map(true);
|
||||
for (id in src._faces) {
|
||||
var sourceFace:Face = src._faces[id];
|
||||
len = sourceFace._vertices.length;
|
||||
var faceVertices:Array = new Array(len);
|
||||
for (i = 0; i < len; i++) {
|
||||
faceVertices[i] = vertexMap[sourceFace._vertices[i]];
|
||||
}
|
||||
var newFace:Face = addFace(faceVertices, id);
|
||||
newFace.aUV = sourceFace._aUV;
|
||||
newFace.bUV = sourceFace._bUV;
|
||||
newFace.cUV = sourceFace._cUV;
|
||||
faceMap[sourceFace] = newFace;
|
||||
}
|
||||
|
||||
// Копирование поверхностей
|
||||
for (id in src._surfaces) {
|
||||
var sourceSurface:Surface = src._surfaces[id];
|
||||
var surfaceFaces:Array = sourceSurface._faces.toArray();
|
||||
len = surfaceFaces.length;
|
||||
for (i = 0; i < len; i++) {
|
||||
surfaceFaces[i] = faceMap[surfaceFaces[i]];
|
||||
}
|
||||
addSurface(surfaceFaces, id).material = SurfaceMaterial(sourceSurface.material.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
696
Alternativa3D5/5.0.0/alternativa/engine3d/core/Object3D.as
Normal file
696
Alternativa3D5/5.0.0/alternativa/engine3d/core/Object3D.as
Normal file
@@ -0,0 +1,696 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.engine3d.errors.Object3DHierarchyError;
|
||||
import alternativa.engine3d.errors.Object3DNotFoundError;
|
||||
import alternativa.types.Matrix3D;
|
||||
import alternativa.types.Point3D;
|
||||
import alternativa.types.Set;
|
||||
import alternativa.utils.ObjectUtils;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Базовый класс для объектов, находящихся в сцене. Класс реализует иерархию объектов сцены, а также содержит сведения
|
||||
* о положении объекта в системе координат родителя.
|
||||
*/
|
||||
public class Object3D {
|
||||
// Операции
|
||||
/**
|
||||
* @private
|
||||
* Поворот или масштабирование
|
||||
*/
|
||||
alternativa3d var changeRotationOrScaleOperation:Operation = new Operation("changeRotationOrScale", this);
|
||||
/**
|
||||
* @private
|
||||
* Перемещение
|
||||
*/
|
||||
alternativa3d var changeCoordsOperation:Operation = new Operation("changeCoords", this);
|
||||
/**
|
||||
* @private
|
||||
* Расчёт матрицы трансформации
|
||||
*/
|
||||
alternativa3d var calculateTransformationOperation:Operation = new Operation("calculateTransformation", this, calculateTransformation, Operation.OBJECT_CALCULATE_TRANSFORMATION);
|
||||
/**
|
||||
* @private
|
||||
* Изменение уровеня мобильности
|
||||
*/
|
||||
alternativa3d var calculateMobilityOperation:Operation = new Operation("calculateMobility", this, calculateMobility, Operation.OBJECT_CALCULATE_MOBILITY);
|
||||
|
||||
// Инкремент количества объектов
|
||||
private static var counter:uint = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Наименование
|
||||
*/
|
||||
alternativa3d var _name:String;
|
||||
/**
|
||||
* @private
|
||||
* Сцена
|
||||
*/
|
||||
alternativa3d var _scene:Scene3D;
|
||||
/**
|
||||
* @private
|
||||
* Родительский объект
|
||||
*/
|
||||
alternativa3d var _parent:Object3D;
|
||||
/**
|
||||
* @private
|
||||
* Дочерние объекты
|
||||
*/
|
||||
alternativa3d var _children:Set = new Set();
|
||||
/**
|
||||
* @private
|
||||
* Уровень мобильности
|
||||
*/
|
||||
alternativa3d var _mobility:int = 0;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alternativa3d var inheritedMobility:int;
|
||||
/**
|
||||
* @private
|
||||
* Координаты объекта относительно родителя
|
||||
*/
|
||||
alternativa3d var _coords:Point3D = new Point3D();
|
||||
/**
|
||||
* @private
|
||||
* Поворот объекта по оси X относительно родителя. Угол измеряется в радианах.
|
||||
*/
|
||||
alternativa3d var _rotationX:Number = 0;
|
||||
/**
|
||||
* @private
|
||||
* Поворот объекта по оси Y относительно родителя. Угол измеряется в радианах.
|
||||
*/
|
||||
alternativa3d var _rotationY:Number = 0;
|
||||
/**
|
||||
* @private
|
||||
* Поворот объекта по оси Z относительно родителя. Угол измеряется в радианах.
|
||||
*/
|
||||
alternativa3d var _rotationZ:Number = 0;
|
||||
/**
|
||||
* @private
|
||||
* Мастшаб объекта по оси X относительно родителя
|
||||
*/
|
||||
alternativa3d var _scaleX:Number = 1;
|
||||
/**
|
||||
* @private
|
||||
* Мастшаб объекта по оси Y относительно родителя
|
||||
*/
|
||||
alternativa3d var _scaleY:Number = 1;
|
||||
/**
|
||||
* @private
|
||||
* Мастшаб объекта по оси Z относительно родителя
|
||||
*/
|
||||
alternativa3d var _scaleZ:Number = 1;
|
||||
/**
|
||||
* @private
|
||||
* Полная матрица трансформации, переводящая координаты из локальной системы координат объекта в систему координат сцены
|
||||
*/
|
||||
alternativa3d var transformation:Matrix3D = new Matrix3D();
|
||||
/**
|
||||
* @private
|
||||
* Координаты в сцене
|
||||
*/
|
||||
alternativa3d var globalCoords:Point3D = new Point3D();
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param name имя объекта
|
||||
*/
|
||||
public function Object3D(name:String = null) {
|
||||
// Имя по-умолчанию
|
||||
_name = (name != null) ? name : defaultName();
|
||||
|
||||
// Последствия операций
|
||||
changeRotationOrScaleOperation.addSequel(calculateTransformationOperation);
|
||||
changeCoordsOperation.addSequel(calculateTransformationOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчёт трансформации
|
||||
*/
|
||||
private function calculateTransformation():void {
|
||||
if (changeRotationOrScaleOperation.queued) {
|
||||
// Если полная трансформация
|
||||
transformation.toTransform(_coords.x, _coords.y, _coords.z, _rotationX, _rotationY, _rotationZ, _scaleX, _scaleY, _scaleZ);
|
||||
if (_parent != null) {
|
||||
transformation.combine(_parent.transformation);
|
||||
}
|
||||
// Сохраняем глобальные координаты объекта
|
||||
globalCoords.x = transformation.d;
|
||||
globalCoords.y = transformation.h;
|
||||
globalCoords.z = transformation.l;
|
||||
} else {
|
||||
// Если только перемещение
|
||||
globalCoords.copy(_coords);
|
||||
if (_parent != null) {
|
||||
globalCoords.transform(_parent.transformation);
|
||||
}
|
||||
transformation.offset(globalCoords.x, globalCoords.y, globalCoords.z);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Расчёт общей мобильности
|
||||
*/
|
||||
private function calculateMobility():void {
|
||||
inheritedMobility = ((_parent != null) ? _parent.inheritedMobility : 0) + _mobility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавление дочернего объекта.
|
||||
* Добавляемый объект удаляется из списка детей предыдущего родителя.
|
||||
* Новой сценой дочернего объекта становится сцена родителя.
|
||||
*
|
||||
* @param child дочерний объект
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.Object3DLevelError попытка добавить дочерний объект, который не может быть связан с данным родителем
|
||||
*/
|
||||
public function addChild(child:Object3D):void {
|
||||
|
||||
// Проверка на null
|
||||
if (child == null) {
|
||||
throw new Object3DHierarchyError(null, this);
|
||||
}
|
||||
|
||||
// Проверка на наличие
|
||||
if (child._parent == this) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Проверка на добавление к самому себе
|
||||
if (child == this) {
|
||||
throw new Object3DHierarchyError(this, this);
|
||||
}
|
||||
|
||||
// Проверка на добавление родительского объекта
|
||||
if (child._scene == _scene) {
|
||||
// Если объект был в той же сцене, либо оба не были в сцене
|
||||
var parentObject:Object3D = _parent;
|
||||
while (parentObject != null) {
|
||||
if (child == parentObject) {
|
||||
throw new Object3DHierarchyError(child, this);
|
||||
return;
|
||||
}
|
||||
parentObject = parentObject._parent;
|
||||
}
|
||||
}
|
||||
|
||||
// Если объект был в другом объекте
|
||||
if (child._parent != null) {
|
||||
// Удалить его оттуда
|
||||
child._parent._children.remove(child);
|
||||
} else {
|
||||
// Если объект был корневым в сцене
|
||||
if (child._scene != null && child._scene._root == child) {
|
||||
child._scene.root = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Добавляем в список
|
||||
_children.add(child);
|
||||
// Указываем себя как родителя
|
||||
child.setParent(this);
|
||||
// Устанавливаем уровни
|
||||
child.setLevel(calculateTransformationOperation.level + 1);
|
||||
// Указываем сцену
|
||||
child.setScene(_scene);
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаление дочернего объекта.
|
||||
*
|
||||
* @param child дочерний объект
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.Object3DNotFoundError попытка удалить несуществующий дочерний объект
|
||||
*/
|
||||
public function removeChild(child:Object3D):void {
|
||||
// Проверка на null
|
||||
if (child == null) {
|
||||
throw new Object3DNotFoundError(null, this);
|
||||
}
|
||||
// Проверка на наличие
|
||||
if (child._parent != this) {
|
||||
throw new Object3DNotFoundError(child, this);
|
||||
}
|
||||
// Убираем из списка
|
||||
_children.remove(child);
|
||||
// Удаляем ссылку на родителя
|
||||
child.setParent(null);
|
||||
// Удаляем ссылку на сцену
|
||||
child.setScene(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Установка родительского объекта.
|
||||
*
|
||||
* @param value родительский объект
|
||||
*/
|
||||
alternativa3d function setParent(value:Object3D):void {
|
||||
// Отписываемся от сигналов старого родителя
|
||||
if (_parent != null) {
|
||||
removeParentSequels();
|
||||
}
|
||||
// Сохранить родителя
|
||||
_parent = value;
|
||||
// Если устанавливаем родителя
|
||||
if (value != null) {
|
||||
// Подписка на сигналы родителя
|
||||
addParentSequels();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Установка новой сцены для объекта.
|
||||
*
|
||||
* @param value сцена
|
||||
*/
|
||||
alternativa3d function setScene(value:Scene3D):void {
|
||||
if (_scene != value) {
|
||||
// Если была сцена
|
||||
if (_scene != null) {
|
||||
// Удалиться из сцены
|
||||
removeFromScene(_scene);
|
||||
}
|
||||
// Если новая сцена
|
||||
if (value != null) {
|
||||
// Добавиться на сцену
|
||||
addToScene(value);
|
||||
}
|
||||
// Сохранить сцену
|
||||
_scene = value;
|
||||
} else {
|
||||
// Посылаем операцию трансформации
|
||||
addOperationToScene(changeRotationOrScaleOperation);
|
||||
// Посылаем операцию пересчёта мобильности
|
||||
addOperationToScene(calculateMobilityOperation);
|
||||
}
|
||||
// Установить эту сцену у дочерних объектов
|
||||
for (var key:* in _children) {
|
||||
var object:Object3D = key;
|
||||
object.setScene(_scene);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Установка уровня операции трансформации.
|
||||
*
|
||||
* @param value уровень операции трансформации
|
||||
*/
|
||||
alternativa3d function setLevel(value:uint):void {
|
||||
// Установить уровень операции трансформации и расчёта мобильности
|
||||
calculateTransformationOperation.level = value;
|
||||
calculateMobilityOperation.level = value;
|
||||
// Установить уровни у дочерних объектов
|
||||
for (var key:* in _children) {
|
||||
var object:Object3D = key;
|
||||
object.setLevel(value + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Подписка на сигналы родителя.
|
||||
*/
|
||||
private function addParentSequels():void {
|
||||
_parent.changeCoordsOperation.addSequel(changeCoordsOperation);
|
||||
_parent.changeRotationOrScaleOperation.addSequel(changeRotationOrScaleOperation);
|
||||
_parent.calculateMobilityOperation.addSequel(calculateMobilityOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление подписки на сигналы родителя.
|
||||
*/
|
||||
private function removeParentSequels():void {
|
||||
_parent.changeCoordsOperation.removeSequel(changeCoordsOperation);
|
||||
_parent.changeRotationOrScaleOperation.removeSequel(changeRotationOrScaleOperation);
|
||||
_parent.calculateMobilityOperation.removeSequel(calculateMobilityOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод вызывается при добавлении объекта на сцену. Наследники могут переопределять метод для выполнения
|
||||
* специфических действий.
|
||||
*
|
||||
* @param scene сцена, в которую добавляется объект
|
||||
*/
|
||||
protected function addToScene(scene:Scene3D):void {
|
||||
// При добавлении на сцену полная трансформация и расчёт мобильности
|
||||
scene.addOperation(changeRotationOrScaleOperation);
|
||||
scene.addOperation(calculateMobilityOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод вызывается при удалении объекта со сцены. Наследники могут переопределять метод для выполнения
|
||||
* специфических действий.
|
||||
*
|
||||
* @param scene сцена, из которой удаляется объект
|
||||
*/
|
||||
protected function removeFromScene(scene:Scene3D):void {
|
||||
// Удаляем все операции из очереди
|
||||
scene.removeOperation(changeRotationOrScaleOperation);
|
||||
scene.removeOperation(changeCoordsOperation);
|
||||
scene.removeOperation(calculateMobilityOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Наименование объекта.
|
||||
*/
|
||||
public function get name():String {
|
||||
return _name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set name(value:String):void {
|
||||
_name = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Сцена, на которой находится объект.
|
||||
*/
|
||||
public function get scene():Scene3D {
|
||||
return _scene;
|
||||
}
|
||||
|
||||
/**
|
||||
* Родительский объект.
|
||||
*/
|
||||
public function get parent():Object3D {
|
||||
return _parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Набор дочерних объектов.
|
||||
*/
|
||||
public function get children():Set {
|
||||
return _children.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Уровень мобильности. Результирующая мобильность объекта является суммой мобильностей объекта и всех его предков
|
||||
* по иерархии объектов в сцене.
|
||||
*/
|
||||
public function get mobility():int {
|
||||
return _mobility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set mobility(value:int):void {
|
||||
if (_mobility != value) {
|
||||
_mobility = value;
|
||||
addOperationToScene(calculateMobilityOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Координата X.
|
||||
*/
|
||||
public function get x():Number {
|
||||
return _coords.x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Координата Y.
|
||||
*/
|
||||
public function get y():Number {
|
||||
return _coords.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Координата Z.
|
||||
*/
|
||||
public function get z():Number {
|
||||
return _coords.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set x(value:Number):void {
|
||||
if (_coords.x != value) {
|
||||
_coords.x = value;
|
||||
addOperationToScene(changeCoordsOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set y(value:Number):void {
|
||||
if (_coords.y != value) {
|
||||
_coords.y = value;
|
||||
addOperationToScene(changeCoordsOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set z(value:Number):void {
|
||||
if (_coords.z != value) {
|
||||
_coords.z = value;
|
||||
addOperationToScene(changeCoordsOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Координаты объекта.
|
||||
*/
|
||||
public function get coords():Point3D {
|
||||
return _coords.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set coords(value:Point3D):void {
|
||||
if (!_coords.equals(value)) {
|
||||
_coords.copy(value);
|
||||
addOperationToScene(changeCoordsOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Угол поворота вокруг оси X, заданный в радианах.
|
||||
*/
|
||||
public function get rotationX():Number {
|
||||
return _rotationX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Угол поворота вокруг оси Y, заданный в радианах.
|
||||
*/
|
||||
public function get rotationY():Number {
|
||||
return _rotationY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Угол поворота вокруг оси Z, заданный в радианах.
|
||||
*/
|
||||
public function get rotationZ():Number {
|
||||
return _rotationZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set rotationX(value:Number):void {
|
||||
if (_rotationX != value) {
|
||||
_rotationX = value;
|
||||
addOperationToScene(changeRotationOrScaleOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set rotationY(value:Number):void {
|
||||
if (_rotationY != value) {
|
||||
_rotationY = value;
|
||||
addOperationToScene(changeRotationOrScaleOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set rotationZ(value:Number):void {
|
||||
if (_rotationZ != value) {
|
||||
_rotationZ = value;
|
||||
addOperationToScene(changeRotationOrScaleOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Коэффициент масштабирования вдоль оси X.
|
||||
*/
|
||||
public function get scaleX():Number {
|
||||
return _scaleX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Коэффициент масштабирования вдоль оси Y.
|
||||
*/
|
||||
public function get scaleY():Number {
|
||||
return _scaleY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Коэффициент масштабирования вдоль оси Z.
|
||||
*/
|
||||
public function get scaleZ():Number {
|
||||
return _scaleZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set scaleX(value:Number):void {
|
||||
if (_scaleX != value) {
|
||||
_scaleX = value;
|
||||
addOperationToScene(changeRotationOrScaleOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set scaleY(value:Number):void {
|
||||
if (_scaleY != value) {
|
||||
_scaleY = value;
|
||||
addOperationToScene(changeRotationOrScaleOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set scaleZ(value:Number):void {
|
||||
if (_scaleZ != value) {
|
||||
_scaleZ = value;
|
||||
addOperationToScene(changeRotationOrScaleOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Строковое представление объекта.
|
||||
*
|
||||
* @return строковое представление объекта
|
||||
*/
|
||||
public function toString():String {
|
||||
return "[" + ObjectUtils.getClassName(this) + " " + _name + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Имя объекта по умолчанию.
|
||||
*
|
||||
* @return имя объекта по умолчанию
|
||||
*/
|
||||
protected function defaultName():String {
|
||||
return "object" + ++counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Добавление операции в очередь.
|
||||
*
|
||||
* @param operation добавляемая операция
|
||||
*/
|
||||
alternativa3d function addOperationToScene(operation:Operation):void {
|
||||
if (_scene != null) {
|
||||
_scene.addOperation(operation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление операции из очереди.
|
||||
*
|
||||
* @param operation удаляемая операция
|
||||
*/
|
||||
alternativa3d function removeOperationFromScene(operation:Operation):void {
|
||||
if (_scene != null) {
|
||||
_scene.removeOperation(operation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Создание пустого объекта без какой-либо внутренней структуры. Например, если некоторый геометрический примитив при
|
||||
* своём создании формирует набор вершин, граней и поверхностей, то этот метод не должен создавать вершины, грани и
|
||||
* поверхности. Данный метод используется в методе clone() и должен быть переопределён в потомках для получения
|
||||
* правильного объекта.
|
||||
*
|
||||
* @return новый пустой объект
|
||||
*/
|
||||
protected function createEmptyObject():Object3D {
|
||||
return new Object3D();
|
||||
}
|
||||
|
||||
/**
|
||||
* Копирование свойств объекта-источника. Данный метод используется в методе clone() и должен быть переопределён в
|
||||
* потомках для получения правильного объекта. Каждый потомок должен в переопределённом методе копировать только те
|
||||
* свойства, которые добавлены к базовому классу именно в нём. Копирование унаследованных свойств выполняется
|
||||
* вызовом super.clonePropertiesFrom(source).
|
||||
*
|
||||
* @param source объект, свойства которого копируются
|
||||
*/
|
||||
protected function clonePropertiesFrom(source:Object3D):void {
|
||||
_name = source._name;
|
||||
_mobility = source._mobility;
|
||||
_coords.x = source._coords.x;
|
||||
_coords.y = source._coords.y;
|
||||
_coords.z = source._coords.z;
|
||||
_rotationX = source._rotationX;
|
||||
_rotationY = source._rotationY;
|
||||
_rotationZ = source._rotationZ;
|
||||
_scaleX = source._scaleX;
|
||||
_scaleY = source._scaleY;
|
||||
_scaleZ = source._scaleZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Клонирование объекта.
|
||||
*
|
||||
* @return клон объекта
|
||||
*/
|
||||
public function clone():Object3D {
|
||||
var copy:Object3D = createEmptyObject();
|
||||
copy.clonePropertiesFrom(this);
|
||||
|
||||
// Клонирование детей
|
||||
for (var key:* in _children) {
|
||||
var child:Object3D = key;
|
||||
copy.addChild(child.clone());
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение дочернего объекта с заданным именем.
|
||||
*
|
||||
* @param name имя дочернего объекта
|
||||
* @return любой дочерний объект с заданным именем или <code>null</code> в случае отсутствия таких объектов
|
||||
*/
|
||||
public function getChildByName(name:String):Object3D {
|
||||
for (var key:* in _children) {
|
||||
var child:Object3D = key;
|
||||
if (child._name == name) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
130
Alternativa3D5/5.0.0/alternativa/engine3d/core/Operation.as
Normal file
130
Alternativa3D5/5.0.0/alternativa/engine3d/core/Operation.as
Normal file
@@ -0,0 +1,130 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.types.Set;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class Operation {
|
||||
|
||||
alternativa3d static const OBJECT_CALCULATE_TRANSFORMATION:uint = 1;
|
||||
alternativa3d static const OBJECT_CALCULATE_MOBILITY:uint = 2;
|
||||
alternativa3d static const VERTEX_CALCULATE_COORDS:uint = 3;
|
||||
alternativa3d static const FACE_CALCULATE_NORMAL:uint = 4;
|
||||
alternativa3d static const FACE_CALCULATE_UV:uint = 5;
|
||||
alternativa3d static const FACE_UPDATE_PRIMITIVE:uint = 6;
|
||||
alternativa3d static const SCENE_CALCULATE_BSP:uint = 7;
|
||||
alternativa3d static const FACE_UPDATE_MATERIAL:uint = 8;
|
||||
alternativa3d static const FACE_CALCULATE_FRAGMENTS_UV:uint = 9;
|
||||
alternativa3d static const CAMERA_CALCULATE_MATRIX:uint = 10;
|
||||
alternativa3d static const CAMERA_CALCULATE_PLANES:uint = 11;
|
||||
alternativa3d static const CAMERA_RENDER:uint = 12;
|
||||
alternativa3d static const SCENE_CLEAR_PRIMITIVES:uint = 13;
|
||||
|
||||
// Объект
|
||||
alternativa3d var object:Object;
|
||||
|
||||
// Метод
|
||||
alternativa3d var method:Function;
|
||||
|
||||
// Название метода
|
||||
alternativa3d var name:String;
|
||||
|
||||
// Последствия
|
||||
private var sequel:Operation;
|
||||
private var sequels:Set;
|
||||
|
||||
// Приоритет операции
|
||||
public var priority:uint;
|
||||
|
||||
// Уровень объекта (необязательный)
|
||||
public var level:uint = 0;
|
||||
|
||||
// Находится ли операция в очереди
|
||||
alternativa3d var queued:Boolean = false;
|
||||
|
||||
public function Operation(name:String, object:Object = null, method:Function = null, priority:uint = 0, level:uint = 0) {
|
||||
this.object = object;
|
||||
this.method = method;
|
||||
this.name = name;
|
||||
this.priority = priority;
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
// Добавить последствие
|
||||
alternativa3d function addSequel(operation:Operation):void {
|
||||
if (sequel == null) {
|
||||
if (sequels == null) {
|
||||
sequel = operation;
|
||||
} else {
|
||||
sequels[operation] = true;
|
||||
}
|
||||
} else {
|
||||
if (sequel != operation) {
|
||||
sequels = new Set(true);
|
||||
sequels[sequel] = true;
|
||||
sequels[operation] = true;
|
||||
sequel = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Удалить последствие
|
||||
alternativa3d function removeSequel(operation:Operation):void {
|
||||
if (sequel == null) {
|
||||
if (sequels != null) {
|
||||
delete sequels[operation];
|
||||
var key:*;
|
||||
var single:Boolean = false;
|
||||
for (key in sequels) {
|
||||
if (single) {
|
||||
single = false;
|
||||
break;
|
||||
}
|
||||
single = true;
|
||||
}
|
||||
if (single) {
|
||||
sequel = key;
|
||||
sequels = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (sequel == operation) {
|
||||
sequel = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
alternativa3d function collectSequels(collector:Array):void {
|
||||
if (sequel == null) {
|
||||
// Проверяем последствия
|
||||
for (var key:* in sequels) {
|
||||
var operation:Operation = key;
|
||||
// Если операция ещё не в очереди
|
||||
if (!operation.queued) {
|
||||
// Добавляем её в очередь
|
||||
collector.push(operation);
|
||||
// Устанавливаем флаг очереди
|
||||
operation.queued = true;
|
||||
// Вызываем добавление в очередь её последствий
|
||||
operation.collectSequels(collector);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!sequel.queued) {
|
||||
collector.push(sequel);
|
||||
sequel.queued = true;
|
||||
sequel.collectSequels(collector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function toString():String {
|
||||
return "[Operation " + priority + "/" + level + " " + object + "." + name + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.*;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class PolyPrimitive {
|
||||
|
||||
// Количество точек
|
||||
alternativa3d var num:uint;
|
||||
// Точки
|
||||
alternativa3d var points:Array = new Array();
|
||||
// UV-координаты
|
||||
alternativa3d var uvs:Array = new Array();
|
||||
|
||||
// Грань
|
||||
alternativa3d var face:Face;
|
||||
// Родительский примитив
|
||||
alternativa3d var parent:PolyPrimitive;
|
||||
// Соседний примитив (при наличии родительского)
|
||||
alternativa3d var sibling:PolyPrimitive;
|
||||
|
||||
// Фрагменты
|
||||
alternativa3d var fragment1:PolyPrimitive;
|
||||
alternativa3d var fragment2:PolyPrimitive;
|
||||
// Рассечения
|
||||
alternativa3d var splitTime1:Number;
|
||||
alternativa3d var splitTime2:Number;
|
||||
|
||||
// BSP-нода, в которой находится примитив
|
||||
alternativa3d var node:BSPNode;
|
||||
|
||||
// Значения для расчёта качества сплиттера
|
||||
alternativa3d var splits:uint;
|
||||
alternativa3d var disbalance:int;
|
||||
// Качество примитива как сплиттера (меньше - лучше)
|
||||
public var splitQuality:Number;
|
||||
|
||||
// Приоритет в BSP-дереве. Чем ниже мобильность, тем примитив выше в дереве.
|
||||
public var mobility:int;
|
||||
|
||||
// Хранилище неиспользуемых примитивов
|
||||
static private var collector:Array = new Array();
|
||||
|
||||
// Создать примитив
|
||||
static alternativa3d function createPolyPrimitive():PolyPrimitive {
|
||||
// Достаём примитив из коллектора
|
||||
var primitive:PolyPrimitive = collector.pop();
|
||||
// Если коллектор пуст, создаём новый примитив
|
||||
if (primitive == null) {
|
||||
primitive = new PolyPrimitive();
|
||||
}
|
||||
//trace(primitive.num, primitive.points.length, primitive.face, primitive.parent, primitive.sibling, primitive.fragment1, primitive.fragment2, primitive.node);
|
||||
return primitive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Кладёт примитив в коллектор для последующего реиспользования.
|
||||
* Ссылка на грань и массивы точек зачищаются в этом методе.
|
||||
* Ссылки на фрагменты (parent, sibling, back, front) должны быть зачищены перед запуском метода.
|
||||
*
|
||||
* Исключение:
|
||||
* при сборке примитивов в сцене ссылки на back и front зачищаются после запуска метода.
|
||||
*
|
||||
* @param primitive примитив на реиспользование
|
||||
*/
|
||||
static alternativa3d function destroyPolyPrimitive(primitive:PolyPrimitive):void {
|
||||
primitive.face = null;
|
||||
for (var i:uint = 0; i < primitive.num; i++) {
|
||||
primitive.points.pop();
|
||||
primitive.uvs.pop();
|
||||
}
|
||||
collector.push(primitive);
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
return "[Primitive " + face._mesh._name + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
1125
Alternativa3D5/5.0.0/alternativa/engine3d/core/Scene3D.as
Normal file
1125
Alternativa3D5/5.0.0/alternativa/engine3d/core/Scene3D.as
Normal file
File diff suppressed because it is too large
Load Diff
349
Alternativa3D5/5.0.0/alternativa/engine3d/core/Surface.as
Normal file
349
Alternativa3D5/5.0.0/alternativa/engine3d/core/Surface.as
Normal file
@@ -0,0 +1,349 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.engine3d.errors.FaceExistsError;
|
||||
import alternativa.engine3d.errors.FaceNotFoundError;
|
||||
import alternativa.engine3d.errors.InvalidIDError;
|
||||
import alternativa.engine3d.materials.SurfaceMaterial;
|
||||
import alternativa.types.Set;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Поверхность — набор граней, объединённых в группу для удобства работы с их общими свойствами.
|
||||
*/
|
||||
public class Surface {
|
||||
// Операции
|
||||
/**
|
||||
* @private
|
||||
* Изменение набора граней
|
||||
*/
|
||||
alternativa3d var changeFacesOperation:Operation = new Operation("changeFaces", this);
|
||||
/**
|
||||
* @private
|
||||
* Изменение материала
|
||||
*/
|
||||
alternativa3d var changeMaterialOperation:Operation = new Operation("changeMaterial", this);
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Меш
|
||||
*/
|
||||
alternativa3d var _mesh:Mesh;
|
||||
/**
|
||||
* @private
|
||||
* Материал
|
||||
*/
|
||||
alternativa3d var _material:SurfaceMaterial;
|
||||
/**
|
||||
* @private
|
||||
* Грани
|
||||
*/
|
||||
alternativa3d var _faces:Set = new Set();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function Surface() {}
|
||||
|
||||
/**
|
||||
* Добавление грани в поверхность.
|
||||
*
|
||||
* @param face экземпляр класса Face или идентификатор грани в Mesh-объекте
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.FaceNotFoundError грань не найдена в Mesh-оъекте
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError недопустимое значение для идентификатора грани
|
||||
* @throws alternativa.engine3d.errors.FaceExistsError попытка добавить уже существующую грань
|
||||
*
|
||||
* @see Face
|
||||
*/
|
||||
public function addFace(face:Object):void {
|
||||
var byLink:Boolean = face is Face;
|
||||
|
||||
// Проверяем на нахождение поверхности в меше
|
||||
if (_mesh == null) {
|
||||
throw new FaceNotFoundError(face, this);
|
||||
}
|
||||
|
||||
// Проверяем на null
|
||||
if (face == null) {
|
||||
throw new FaceNotFoundError(null, this);
|
||||
}
|
||||
|
||||
// Проверяем наличие грани в меше
|
||||
if (byLink) {
|
||||
// Если удаляем по ссылке
|
||||
if (Face(face)._mesh != _mesh) {
|
||||
// Если грань не в меше
|
||||
throw new FaceNotFoundError(face, this);
|
||||
}
|
||||
} else {
|
||||
// Если удаляем по ID
|
||||
if (_mesh._faces[face] == undefined) {
|
||||
// Если нет грани с таким ID
|
||||
throw new FaceNotFoundError(face, this);
|
||||
} else {
|
||||
if (!(_mesh._faces[face] is Face)) {
|
||||
throw new InvalidIDError(face, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Находим грань
|
||||
var f:Face = byLink ? Face(face) : _mesh._faces[face];
|
||||
|
||||
// Проверяем наличие грани в поверхности
|
||||
if (_faces.has(f)) {
|
||||
// Если грань уже в поверхности
|
||||
throw new FaceExistsError(f, this);
|
||||
}
|
||||
|
||||
// Проверяем грань на нахождение в другой поверхности
|
||||
if (f._surface != null) {
|
||||
// Удаляем её из той поверхности
|
||||
f._surface._faces.remove(f);
|
||||
f.removeFromSurface(f._surface);
|
||||
}
|
||||
|
||||
// Добавляем грань в поверхность
|
||||
_faces.add(f);
|
||||
f.addToSurface(this);
|
||||
|
||||
// Отправляем операцию изменения набора граней
|
||||
_mesh.addOperationToScene(changeFacesOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаление грани из поверхности. Грань может быть задана либо экземпляром класса <code>alternativa.engine3d.core.Face</code>,
|
||||
* либо идентификатором грани в Mesh-объекте.
|
||||
*
|
||||
* @param face экземпляр класса Face или идентификатор грани.
|
||||
*
|
||||
* @throws alternativa.engine3d.errors.FaceNotFoundError face не находится в Surface
|
||||
* @throws alternativa.engine3d.errors.InvalidIDError в качестве face передано зарезервированное значение
|
||||
*
|
||||
* @see Face
|
||||
*/
|
||||
public function removeFace(face:Object):void {
|
||||
var byLink:Boolean = face is Face;
|
||||
|
||||
// Проверяем на нахождение поверхности в меше
|
||||
if (_mesh == null) {
|
||||
throw new FaceNotFoundError(face, this);
|
||||
}
|
||||
|
||||
// Проверяем на null
|
||||
if (face == null) {
|
||||
throw new FaceNotFoundError(null, this);
|
||||
}
|
||||
|
||||
// Проверяем наличие грани в меше
|
||||
if (byLink) {
|
||||
// Если удаляем по ссылке
|
||||
if (Face(face)._mesh != _mesh) {
|
||||
// Если грань не в меше
|
||||
throw new FaceNotFoundError(face, this);
|
||||
}
|
||||
} else {
|
||||
// Если удаляем по ID
|
||||
if (_mesh._faces[face] == undefined) {
|
||||
// Если нет грани с таким ID
|
||||
throw new FaceNotFoundError(face, this);
|
||||
} else {
|
||||
if (!(_mesh._faces[face] is Face)) {
|
||||
throw new InvalidIDError(face, this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Находим грань
|
||||
var f:Face = byLink ? Face(face) : _mesh._faces[face];
|
||||
|
||||
// Проверяем наличие грани в поверхности
|
||||
if (!_faces.has(f)) {
|
||||
// Если грань не в поверхности
|
||||
throw new FaceNotFoundError(f, this);
|
||||
}
|
||||
|
||||
// Удаляем грань из поверхности
|
||||
_faces.remove(f);
|
||||
f.removeFromSurface(this);
|
||||
|
||||
// Отправляем операцию изменения набора граней
|
||||
_mesh.addOperationToScene(changeFacesOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Материал поверхности. При установке нового значения, устанавливаемый материал будет удалён из старой поверхности.
|
||||
*/
|
||||
public function get material():SurfaceMaterial {
|
||||
return _material;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set material(value:SurfaceMaterial):void {
|
||||
if (_material != value) {
|
||||
// Если был материал
|
||||
if (_material != null) {
|
||||
// Удалить материал из поверхности
|
||||
_material.removeFromSurface(this);
|
||||
// Удалить материал из меша
|
||||
if (_mesh != null) {
|
||||
_material.removeFromMesh(_mesh);
|
||||
// Удалить материал из сцены
|
||||
if (_mesh._scene != null) {
|
||||
_material.removeFromScene(_mesh._scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Если новый материал
|
||||
if (value != null) {
|
||||
// Если материал был в другой поверхности
|
||||
if (value._surface != null) {
|
||||
// Удалить его оттуда
|
||||
value._surface.material = null;
|
||||
}
|
||||
// Добавить материал в поверхность
|
||||
value.addToSurface(this);
|
||||
// Добавить материал в меш
|
||||
if (_mesh != null) {
|
||||
value.addToMesh(_mesh);
|
||||
// Добавить материал в сцену
|
||||
if (_mesh._scene != null) {
|
||||
value.addToScene(_mesh._scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Сохраняем материал
|
||||
_material = value;
|
||||
// Отправляем операцию изменения материала
|
||||
addMaterialChangedOperationToScene();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Набор граней поверхности.
|
||||
*/
|
||||
public function get faces():Set {
|
||||
return _faces.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mesh-объект, которому принадлежит поверхность.
|
||||
*/
|
||||
public function get mesh():Mesh {
|
||||
return _mesh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Идентификатор поверхности в Mesh-объекте. В случае, если поверхность не принадлежит ни одному Mesh-объекту,
|
||||
* значение идентификатора равно <code>null</code>.
|
||||
*/
|
||||
public function get id():Object {
|
||||
return (_mesh != null) ? _mesh.getSurfaceId(this) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Добавление в сцену.
|
||||
*
|
||||
* @param scene
|
||||
*/
|
||||
alternativa3d function addToScene(scene:Scene3D):void {
|
||||
// Добавляем на сцену материал
|
||||
if (_material != null) {
|
||||
_material.addToScene(scene);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление из сцены.
|
||||
*
|
||||
* @param scene
|
||||
*/
|
||||
alternativa3d function removeFromScene(scene:Scene3D):void {
|
||||
// Удаляем все операции из очереди
|
||||
scene.removeOperation(changeFacesOperation);
|
||||
scene.removeOperation(changeMaterialOperation);
|
||||
// Удаляем из сцены материал
|
||||
if (_material != null) {
|
||||
_material.removeFromScene(scene);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Добавление к мешу
|
||||
* @param mesh
|
||||
*/
|
||||
alternativa3d function addToMesh(mesh:Mesh):void {
|
||||
// Подписка на операции меша
|
||||
|
||||
// Добавляем в меш материал
|
||||
if (_material != null) {
|
||||
_material.addToMesh(mesh);
|
||||
}
|
||||
// Сохранить меш
|
||||
_mesh = mesh;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление из меша
|
||||
*
|
||||
* @param mesh
|
||||
*/
|
||||
alternativa3d function removeFromMesh(mesh:Mesh):void {
|
||||
// Отписка от операций меша
|
||||
|
||||
// Удаляем из меша материал
|
||||
if (_material != null) {
|
||||
_material.removeFromMesh(mesh);
|
||||
}
|
||||
// Удалить ссылку на меш
|
||||
_mesh = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление граней
|
||||
*/
|
||||
alternativa3d function removeFaces():void {
|
||||
for (var key:* in _faces) {
|
||||
var face:Face = key;
|
||||
_faces.remove(face);
|
||||
face.removeFromSurface(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Изменение материала
|
||||
*/
|
||||
alternativa3d function addMaterialChangedOperationToScene():void {
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(changeMaterialOperation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Строковое представление объекта.
|
||||
* @return строковое представление объекта
|
||||
*/
|
||||
public function toString():String {
|
||||
var length:uint = _faces.length;
|
||||
var res:String = "[Surface ID:" + id + ((length > 0) ? " faces:" : "");
|
||||
var i:uint = 0;
|
||||
for (var key:* in _faces) {
|
||||
var face:Face = key;
|
||||
res += face.id + ((i < length - 1) ? ", " : "");
|
||||
i++;
|
||||
}
|
||||
res += "]";
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
246
Alternativa3D5/5.0.0/alternativa/engine3d/core/Vertex.as
Normal file
246
Alternativa3D5/5.0.0/alternativa/engine3d/core/Vertex.as
Normal file
@@ -0,0 +1,246 @@
|
||||
package alternativa.engine3d.core {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.types.Matrix3D;
|
||||
import alternativa.types.Point3D;
|
||||
import alternativa.types.Set;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Вершина полигона.
|
||||
*/
|
||||
final public class Vertex {
|
||||
// Операции
|
||||
/**
|
||||
* @private
|
||||
* Изменение локальных координат
|
||||
*/
|
||||
alternativa3d var changeCoordsOperation:Operation = new Operation("changeCoords", this);
|
||||
/**
|
||||
* @private
|
||||
* Расчёт глобальных координат
|
||||
*/
|
||||
alternativa3d var calculateCoordsOperation:Operation = new Operation("calculateCoords", this, calculateCoords, Operation.VERTEX_CALCULATE_COORDS);
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Меш
|
||||
*/
|
||||
alternativa3d var _mesh:Mesh;
|
||||
/**
|
||||
* @private
|
||||
* Координаты точки
|
||||
*/
|
||||
alternativa3d var _coords:Point3D;
|
||||
/**
|
||||
* @private
|
||||
* Грани
|
||||
*/
|
||||
alternativa3d var _faces:Set = new Set();
|
||||
/**
|
||||
* @private
|
||||
* Координаты в сцене
|
||||
*/
|
||||
alternativa3d var globalCoords:Point3D = new Point3D();
|
||||
|
||||
/**
|
||||
* Создание экземпляра вершины.
|
||||
*
|
||||
* @param x координата вершины по оси X
|
||||
* @param y координата вершины по оси Y
|
||||
* @param z координата вершины по оси Z
|
||||
*/
|
||||
public function Vertex(x:Number = 0, y:Number = 0, z:Number = 0) {
|
||||
_coords = new Point3D(x, y, z);
|
||||
|
||||
// Изменение координат инициирует пересчёт глобальных координат
|
||||
changeCoordsOperation.addSequel(calculateCoordsOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Вызывается из операции calculateCoordsOperation для расчета глобальных координат вершины
|
||||
*/
|
||||
private function calculateCoords():void {
|
||||
globalCoords.copy(_coords);
|
||||
globalCoords.transform(_mesh.transformation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set x(value:Number):void {
|
||||
if (_coords.x != value) {
|
||||
_coords.x = value;
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(changeCoordsOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set y(value:Number):void {
|
||||
if (_coords.y != value) {
|
||||
_coords.y = value;
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(changeCoordsOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set z(value:Number):void {
|
||||
if (_coords.z != value) {
|
||||
_coords.z = value;
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(changeCoordsOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set coords(value:Point3D):void {
|
||||
if (!_coords.equals(value)) {
|
||||
_coords.copy(value);
|
||||
if (_mesh != null) {
|
||||
_mesh.addOperationToScene(changeCoordsOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* координата вершины по оси X.
|
||||
*/
|
||||
public function get x():Number {
|
||||
return _coords.x;
|
||||
}
|
||||
|
||||
/**
|
||||
* координата вершины по оси Y.
|
||||
*/
|
||||
public function get y():Number {
|
||||
return _coords.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* координата вершины по оси Z.
|
||||
*/
|
||||
public function get z():Number {
|
||||
return _coords.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Координаты вершины.
|
||||
*/
|
||||
public function get coords():Point3D {
|
||||
return _coords.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mesh-объект, в котором находится вершина.
|
||||
*/
|
||||
public function get mesh():Mesh {
|
||||
return _mesh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Множество граней, которым принадлежит вершина.
|
||||
*/
|
||||
public function get faces():Set {
|
||||
return _faces.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Идентификатор вершины в Mesh-объекте. Если вершина не находится в Mesh-объекте, возвращается <code>null</code>.
|
||||
* @see #mesh
|
||||
*/
|
||||
public function get id():Object {
|
||||
return (_mesh != null) ? _mesh.getVertexId(this) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param scene
|
||||
*/
|
||||
alternativa3d function addToScene(scene:Scene3D):void {
|
||||
// При добавлении на сцену расчитать глобальные координаты
|
||||
scene.addOperation(calculateCoordsOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param scene
|
||||
*/
|
||||
alternativa3d function removeFromScene(scene:Scene3D):void {
|
||||
// Удаляем все операции из очереди
|
||||
scene.removeOperation(calculateCoordsOperation);
|
||||
scene.removeOperation(changeCoordsOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param mesh
|
||||
*/
|
||||
alternativa3d function addToMesh(mesh:Mesh):void {
|
||||
// Подписка на операции меша
|
||||
mesh.changeCoordsOperation.addSequel(calculateCoordsOperation);
|
||||
mesh.changeRotationOrScaleOperation.addSequel(calculateCoordsOperation);
|
||||
// Сохранить меш
|
||||
_mesh = mesh;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param mesh
|
||||
*/
|
||||
alternativa3d function removeFromMesh(mesh:Mesh):void {
|
||||
// Отписка от операций меша
|
||||
mesh.changeCoordsOperation.removeSequel(calculateCoordsOperation);
|
||||
mesh.changeRotationOrScaleOperation.removeSequel(calculateCoordsOperation);
|
||||
// Удалить зависимые грани
|
||||
for (var key:* in _faces) {
|
||||
var face:Face = key;
|
||||
mesh.removeFace(face);
|
||||
}
|
||||
// Удалить ссылку на меш
|
||||
_mesh = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param face
|
||||
*/
|
||||
alternativa3d function addToFace(face:Face):void {
|
||||
// Подписка грани на операции
|
||||
changeCoordsOperation.addSequel(face.calculateUVOperation);
|
||||
changeCoordsOperation.addSequel(face.calculateNormalOperation);
|
||||
// Добавить грань в список
|
||||
_faces.add(face);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param face
|
||||
*/
|
||||
alternativa3d function removeFromFace(face:Face):void {
|
||||
// Отписка грани от операций
|
||||
changeCoordsOperation.removeSequel(face.calculateUVOperation);
|
||||
changeCoordsOperation.removeSequel(face.calculateNormalOperation);
|
||||
// Удалить грань из списка
|
||||
_faces.remove(face);
|
||||
}
|
||||
|
||||
/**
|
||||
* Строковое представление объекта.
|
||||
* @return строковое представление объекта
|
||||
*/
|
||||
public function toString():String {
|
||||
return "[Vertex ID:" + id + " " + _coords.x.toFixed(2) + ", " + _coords.y.toFixed(2) + ", " + _coords.z.toFixed(2) + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 98
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/display
|
||||
END
|
||||
Skin.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 106
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/display/Skin.as
|
||||
END
|
||||
View.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 106
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/display/View.as
|
||||
END
|
||||
@@ -0,0 +1,52 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/display
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
Skin.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
50eeea2690535925fcfe7ade11eaf05a
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
View.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
3a3fa659a8422daf7f19c54ac7a2470f
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
8
|
||||
@@ -0,0 +1,68 @@
|
||||
package alternativa.engine3d.display {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Face;
|
||||
import alternativa.engine3d.core.Operation;
|
||||
import alternativa.engine3d.core.PolyPrimitive;
|
||||
import alternativa.engine3d.materials.SurfaceMaterial;
|
||||
|
||||
import flash.display.Graphics;
|
||||
import flash.display.Sprite;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class Skin extends Sprite {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Графика скина (для быстрого доступа)
|
||||
*/
|
||||
alternativa3d var gfx:Graphics = graphics;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Ссылка на следующий скин
|
||||
*/
|
||||
alternativa3d var nextSkin:Skin;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Примитив
|
||||
*/
|
||||
alternativa3d var primitive:PolyPrimitive;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Материал
|
||||
*/
|
||||
alternativa3d var material:SurfaceMaterial;
|
||||
|
||||
// Хранилище неиспользуемых скинов
|
||||
static private var collector:Array = new Array();
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Создание скина.
|
||||
*/
|
||||
static alternativa3d function createSkin():Skin {
|
||||
// Достаём скин из коллектора
|
||||
var skin:Skin = collector.pop();
|
||||
// Если коллектор пуст, создаём новый скин
|
||||
if (skin == null) {
|
||||
skin = new Skin();
|
||||
}
|
||||
return skin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление скина, все ссылки должны быть почищены.
|
||||
*/
|
||||
static alternativa3d function destroySkin(skin:Skin):void {
|
||||
collector.push(skin);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package alternativa.engine3d.display {
|
||||
import alternativa.engine3d.*;
|
||||
|
||||
import flash.display.Sprite;
|
||||
import flash.geom.Rectangle;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Область для вывода изображения с камеры. Для отображения сцены через камеру, необходимо назначить камере область вывода.
|
||||
*/
|
||||
public class View extends Sprite {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Область отрисовки спрайтов
|
||||
*/
|
||||
alternativa3d var canvas:Sprite;
|
||||
|
||||
private var _camera:Camera3D;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Ширина области вывода
|
||||
*/
|
||||
alternativa3d var _width:Number;
|
||||
/**
|
||||
* @private
|
||||
* Высота области вывода
|
||||
*/
|
||||
alternativa3d var _height:Number;
|
||||
|
||||
/**
|
||||
* Создание экземпляра области вывода изображения камеры.
|
||||
*
|
||||
* @param camera камера, изображение с которой должно выводиться
|
||||
* @param width ширина области вывода
|
||||
* @param height высота области вывода
|
||||
*/
|
||||
public function View(camera:Camera3D = null, width:Number = 0, height:Number = 0) {
|
||||
canvas = new Sprite();
|
||||
canvas.mouseEnabled = false;
|
||||
canvas.mouseChildren = false;
|
||||
canvas.tabEnabled = false;
|
||||
canvas.tabChildren = false;
|
||||
addChild(canvas);
|
||||
|
||||
this.camera = camera;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Камера с которой ведется отображение.
|
||||
*/
|
||||
public function get camera():Camera3D {
|
||||
return _camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set camera(value:Camera3D):void {
|
||||
if (_camera != value) {
|
||||
// Если была камера
|
||||
if (_camera != null) {
|
||||
// Удалить камеру
|
||||
_camera.removeFromView(this);
|
||||
}
|
||||
// Если новая камера
|
||||
if (value != null) {
|
||||
// Если камера была в другом вьюпорте
|
||||
if (value._view != null) {
|
||||
// Удалить её оттуда
|
||||
value._view.camera = null;
|
||||
}
|
||||
// Добавить камеру
|
||||
value.addToView(this);
|
||||
} else {
|
||||
// Зачистка скинов
|
||||
if (canvas.numChildren > 0) {
|
||||
var skin:Skin = Skin(canvas.getChildAt(0));
|
||||
while (skin != null) {
|
||||
// Сохраняем следующий
|
||||
var next:Skin = skin.nextSkin;
|
||||
// Удаляем из канваса
|
||||
canvas.removeChild(skin);
|
||||
// Очистка скина
|
||||
if (skin.material != null) {
|
||||
skin.material.clear(skin);
|
||||
}
|
||||
// Зачищаем ссылки
|
||||
skin.nextSkin = null;
|
||||
skin.primitive = null;
|
||||
skin.material = null;
|
||||
// Удаляем
|
||||
Skin.destroySkin(skin);
|
||||
// Следующий устанавливаем текущим
|
||||
skin = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Сохраняем камеру
|
||||
_camera = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ширина области вывода в пикселях.
|
||||
*/
|
||||
override public function get width():Number {
|
||||
return _width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override public function set width(value:Number):void {
|
||||
if (_width != value) {
|
||||
_width = value;
|
||||
canvas.x = _width*0.5;
|
||||
if (_camera != null) {
|
||||
camera.addOperationToScene(camera.calculatePlanesOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Высота области вывода в пикселях.
|
||||
*/
|
||||
override public function get height():Number {
|
||||
return _height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override public function set height(value:Number):void {
|
||||
if (_height != value) {
|
||||
_height = value;
|
||||
canvas.y = _height*0.5;
|
||||
if (_camera != null) {
|
||||
camera.addOperationToScene(camera.calculatePlanesOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
68
Alternativa3D5/5.0.0/alternativa/engine3d/display/Skin.as
Normal file
68
Alternativa3D5/5.0.0/alternativa/engine3d/display/Skin.as
Normal file
@@ -0,0 +1,68 @@
|
||||
package alternativa.engine3d.display {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
import alternativa.engine3d.core.Face;
|
||||
import alternativa.engine3d.core.Operation;
|
||||
import alternativa.engine3d.core.PolyPrimitive;
|
||||
import alternativa.engine3d.materials.SurfaceMaterial;
|
||||
|
||||
import flash.display.Graphics;
|
||||
import flash.display.Sprite;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public class Skin extends Sprite {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Графика скина (для быстрого доступа)
|
||||
*/
|
||||
alternativa3d var gfx:Graphics = graphics;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Ссылка на следующий скин
|
||||
*/
|
||||
alternativa3d var nextSkin:Skin;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Примитив
|
||||
*/
|
||||
alternativa3d var primitive:PolyPrimitive;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Материал
|
||||
*/
|
||||
alternativa3d var material:SurfaceMaterial;
|
||||
|
||||
// Хранилище неиспользуемых скинов
|
||||
static private var collector:Array = new Array();
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Создание скина.
|
||||
*/
|
||||
static alternativa3d function createSkin():Skin {
|
||||
// Достаём скин из коллектора
|
||||
var skin:Skin = collector.pop();
|
||||
// Если коллектор пуст, создаём новый скин
|
||||
if (skin == null) {
|
||||
skin = new Skin();
|
||||
}
|
||||
return skin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Удаление скина, все ссылки должны быть почищены.
|
||||
*/
|
||||
static alternativa3d function destroySkin(skin:Skin):void {
|
||||
collector.push(skin);
|
||||
}
|
||||
}
|
||||
}
|
||||
149
Alternativa3D5/5.0.0/alternativa/engine3d/display/View.as
Normal file
149
Alternativa3D5/5.0.0/alternativa/engine3d/display/View.as
Normal file
@@ -0,0 +1,149 @@
|
||||
package alternativa.engine3d.display {
|
||||
import alternativa.engine3d.*;
|
||||
|
||||
import flash.display.Sprite;
|
||||
import flash.geom.Rectangle;
|
||||
import alternativa.engine3d.core.Camera3D;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Область для вывода изображения с камеры. Для отображения сцены через камеру, необходимо назначить камере область вывода.
|
||||
*/
|
||||
public class View extends Sprite {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Область отрисовки спрайтов
|
||||
*/
|
||||
alternativa3d var canvas:Sprite;
|
||||
|
||||
private var _camera:Camera3D;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Ширина области вывода
|
||||
*/
|
||||
alternativa3d var _width:Number;
|
||||
/**
|
||||
* @private
|
||||
* Высота области вывода
|
||||
*/
|
||||
alternativa3d var _height:Number;
|
||||
|
||||
/**
|
||||
* Создание экземпляра области вывода изображения камеры.
|
||||
*
|
||||
* @param camera камера, изображение с которой должно выводиться
|
||||
* @param width ширина области вывода
|
||||
* @param height высота области вывода
|
||||
*/
|
||||
public function View(camera:Camera3D = null, width:Number = 0, height:Number = 0) {
|
||||
canvas = new Sprite();
|
||||
canvas.mouseEnabled = false;
|
||||
canvas.mouseChildren = false;
|
||||
canvas.tabEnabled = false;
|
||||
canvas.tabChildren = false;
|
||||
addChild(canvas);
|
||||
|
||||
this.camera = camera;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Камера с которой ведется отображение.
|
||||
*/
|
||||
public function get camera():Camera3D {
|
||||
return _camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set camera(value:Camera3D):void {
|
||||
if (_camera != value) {
|
||||
// Если была камера
|
||||
if (_camera != null) {
|
||||
// Удалить камеру
|
||||
_camera.removeFromView(this);
|
||||
}
|
||||
// Если новая камера
|
||||
if (value != null) {
|
||||
// Если камера была в другом вьюпорте
|
||||
if (value._view != null) {
|
||||
// Удалить её оттуда
|
||||
value._view.camera = null;
|
||||
}
|
||||
// Добавить камеру
|
||||
value.addToView(this);
|
||||
} else {
|
||||
// Зачистка скинов
|
||||
if (canvas.numChildren > 0) {
|
||||
var skin:Skin = Skin(canvas.getChildAt(0));
|
||||
while (skin != null) {
|
||||
// Сохраняем следующий
|
||||
var next:Skin = skin.nextSkin;
|
||||
// Удаляем из канваса
|
||||
canvas.removeChild(skin);
|
||||
// Очистка скина
|
||||
if (skin.material != null) {
|
||||
skin.material.clear(skin);
|
||||
}
|
||||
// Зачищаем ссылки
|
||||
skin.nextSkin = null;
|
||||
skin.primitive = null;
|
||||
skin.material = null;
|
||||
// Удаляем
|
||||
Skin.destroySkin(skin);
|
||||
// Следующий устанавливаем текущим
|
||||
skin = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Сохраняем камеру
|
||||
_camera = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ширина области вывода в пикселях.
|
||||
*/
|
||||
override public function get width():Number {
|
||||
return _width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override public function set width(value:Number):void {
|
||||
if (_width != value) {
|
||||
_width = value;
|
||||
canvas.x = _width*0.5;
|
||||
if (_camera != null) {
|
||||
camera.addOperationToScene(camera.calculatePlanesOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Высота области вывода в пикселях.
|
||||
*/
|
||||
override public function get height():Number {
|
||||
return _height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override public function set height(value:Number):void {
|
||||
if (_height != value) {
|
||||
_height = value;
|
||||
canvas.y = _height*0.5;
|
||||
if (_camera != null) {
|
||||
camera.addOperationToScene(camera.calculatePlanesOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 97
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/errors
|
||||
END
|
||||
InvalidIDError.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 115
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/errors/InvalidIDError.as
|
||||
END
|
||||
ObjectNotFoundError.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 120
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/errors/ObjectNotFoundError.as
|
||||
END
|
||||
SurfaceNotFoundError.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 121
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/errors/SurfaceNotFoundError.as
|
||||
END
|
||||
ObjectExistsError.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 118
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/errors/ObjectExistsError.as
|
||||
END
|
||||
SurfaceExistsError.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 119
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/errors/SurfaceExistsError.as
|
||||
END
|
||||
Object3DNotFoundError.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 122
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/errors/Object3DNotFoundError.as
|
||||
END
|
||||
VertexNotFoundError.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 120
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/errors/VertexNotFoundError.as
|
||||
END
|
||||
FaceNotFoundError.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 118
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/errors/FaceNotFoundError.as
|
||||
END
|
||||
Engine3DError.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 114
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/errors/Engine3DError.as
|
||||
END
|
||||
Object3DHierarchyError.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 123
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/errors/Object3DHierarchyError.as
|
||||
END
|
||||
VertexExistsError.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 118
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/errors/VertexExistsError.as
|
||||
END
|
||||
FaceExistsError.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 116
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/errors/FaceExistsError.as
|
||||
END
|
||||
FaceNeedMoreVerticesError.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 126
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/errors/FaceNeedMoreVerticesError.as
|
||||
END
|
||||
184
Alternativa3D5/5.0.0/alternativa/engine3d/errors/.svn/entries
Normal file
184
Alternativa3D5/5.0.0/alternativa/engine3d/errors/.svn/entries
Normal file
@@ -0,0 +1,184 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/errors
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
InvalidIDError.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
bdff3ad52feb719e00dc92389c9e5e1e
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
ObjectNotFoundError.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
6dd11fc2d6176278fb21fb7172f9ec0f
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
SurfaceNotFoundError.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
420e1e6740301ff2bb70e1a0ae085a75
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
ObjectExistsError.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
a25dfccfb0211e9199fbe392e5f0ff08
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
SurfaceExistsError.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
ac6477b7def1b87a76360823782d1dca
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
Object3DNotFoundError.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
7ab91f78c0b7e42b0ce116e938e62c66
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
VertexNotFoundError.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
2cf926b6a3bedb6035162d2cc1a14201
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
FaceNotFoundError.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
6983fcb77a1b337338f6452e35f504bf
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
Engine3DError.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
47a129612e42512ac074b5273ce9be5d
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
Object3DHierarchyError.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
8f01540730eb38c2b20aa7d3a36cb7f1
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
VertexExistsError.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
93465bfa7fb4dbaca1323d2e09c1c645
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
FaceExistsError.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
6e778f9e94447b3c427f84bad486ae59
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
FaceNeedMoreVerticesError.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
35781dbd32ece10c9efca8ab9749c293
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
8
|
||||
@@ -0,0 +1,27 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.utils.TextUtils;
|
||||
|
||||
/**
|
||||
* Базовый класс для ошибок 3d-engine.
|
||||
*/
|
||||
public class Engine3DError extends Error {
|
||||
|
||||
/**
|
||||
* Источник ошибки - объект в котором произошла ошибка.
|
||||
*/
|
||||
public var source:Object;
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param message описание ошибки
|
||||
* @param source источник ошибки
|
||||
*/
|
||||
public function Engine3DError(message:String = "", source:Object = null) {
|
||||
super(message);
|
||||
this.source = source;
|
||||
this.name = "Engine3DError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.engine3d.core.Face;
|
||||
import alternativa.engine3d.core.Mesh;
|
||||
import alternativa.utils.TextUtils;
|
||||
import alternativa.engine3d.core.Surface;
|
||||
|
||||
/**
|
||||
* Ошибка, возникающая при попытке добавить в какой-либо объект грань, уже содержащуюся в данном объекте.
|
||||
*/
|
||||
public class FaceExistsError extends ObjectExistsError {
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param face экземпляр или идентификатор грани, которая уже содержится в объекте
|
||||
* @param source источник ошибки
|
||||
*/
|
||||
public function FaceExistsError(face:Object = null, source:Object = null) {
|
||||
var message:String;
|
||||
if (source is Mesh) {
|
||||
message = "Mesh ";
|
||||
} else if (source is Surface) {
|
||||
message = "Surface ";
|
||||
}
|
||||
if (face is Face) {
|
||||
message += "%1. Face %2 already exists.";
|
||||
} else {
|
||||
message += "%1. Face with ID '%2' already exists.";
|
||||
}
|
||||
super(TextUtils.insertVars(message, source, face), face, source);
|
||||
this.name = "FaceExistsError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.engine3d.core.Mesh;
|
||||
import alternativa.utils.TextUtils;
|
||||
|
||||
/**
|
||||
* Ошибка, обозначающая недостаточное количество вершин для создания грани.
|
||||
* Для создания грани должно быть указано не менее трех вершин.
|
||||
*/
|
||||
public class FaceNeedMoreVerticesError extends Engine3DError {
|
||||
|
||||
/**
|
||||
* Количество переданных для создания грани вершин
|
||||
*/
|
||||
public var count:uint;
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param mesh объект, в котором произошла ошибка
|
||||
* @param count количество вершин, переданное для создания грани
|
||||
*/
|
||||
public function FaceNeedMoreVerticesError(mesh:Mesh = null, count:uint = 0) {
|
||||
super(TextUtils.insertVars("Mesh %1. %2 vertices not enough for face creation.", mesh, count), mesh);
|
||||
this.count = count;
|
||||
this.name = "FaceNeedMoreVerticesError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.engine3d.core.Face;
|
||||
import alternativa.engine3d.core.Mesh;
|
||||
import alternativa.utils.TextUtils;
|
||||
|
||||
/**
|
||||
* Ошибка, возникающая, если грань не найдена в объекте.
|
||||
*/
|
||||
public class FaceNotFoundError extends ObjectNotFoundError {
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param face экземпляр или идентификатор грани
|
||||
* @param source объект, в котором произошла ошибка
|
||||
*/
|
||||
public function FaceNotFoundError(face:Object = null, source:Object = null) {
|
||||
var message:String;
|
||||
if (source is Mesh) {
|
||||
message = "Mesh ";
|
||||
} else {
|
||||
message = "Surface ";
|
||||
}
|
||||
if (face is Face) {
|
||||
message += "%1. Face %2 not found.";
|
||||
} else {
|
||||
message += "%1. Face with ID '%2' not found.";
|
||||
}
|
||||
super(TextUtils.insertVars(message, source, face), face, source);
|
||||
this.name = "FaceNotFoundError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package alternativa.engine3d.errors {
|
||||
import alternativa.utils.TextUtils;
|
||||
import alternativa.engine3d.core.Mesh;
|
||||
import alternativa.engine3d.core.Surface;
|
||||
|
||||
|
||||
/**
|
||||
* Ошибка, обозначающая, что идентификатор зарезервирован и не может быть использован.
|
||||
*/
|
||||
public class InvalidIDError extends Engine3DError {
|
||||
/**
|
||||
* Зарезервированный идентификатор
|
||||
*/
|
||||
public var id:Object;
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param id идентификатор
|
||||
* @param source объект, в котором произошла ошибка
|
||||
*/
|
||||
public function InvalidIDError(id:Object = null, source:Object = null) {
|
||||
var message:String;
|
||||
if (source is Mesh) {
|
||||
message = "Mesh %2. ";
|
||||
} else if (source is Surface) {
|
||||
message = "Surface %2. ";
|
||||
}
|
||||
super(TextUtils.insertVars(message + "ID %1 is reserved and cannot be used", [id, source]), source);
|
||||
this.id = id;
|
||||
this.name = "InvalidIDError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.utils.TextUtils;
|
||||
|
||||
/**
|
||||
* Ошибка, связанная с нарушением иерархии объектов сцены.
|
||||
*/
|
||||
public class Object3DHierarchyError extends ObjectNotFoundError
|
||||
{
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param object объект, нарушающий иерархию
|
||||
* @param source источник ошибки
|
||||
*/
|
||||
public function Object3DHierarchyError(object:Object3D = null, source:Object3D = null) {
|
||||
super(TextUtils.insertVars("Object3D %1. Object %2 cannot be added", source, object), object, source);
|
||||
this.name = "Object3DHierarchyError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.utils.TextUtils;
|
||||
|
||||
/**
|
||||
* Ошибка, возникающая, когда объект сцены не был найден в списке связанных с необходимым объектом сцены.
|
||||
*/
|
||||
public class Object3DNotFoundError extends ObjectNotFoundError {
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param object ненайденный объект сцены
|
||||
* @param source объект сцены, в котором произошла ошибка
|
||||
*/
|
||||
public function Object3DNotFoundError(object:Object3D = null, source:Object3D = null) {
|
||||
super(TextUtils.insertVars("Object3D %1. Object %2 not in child list", source, object), object, source);
|
||||
this.name = "Object3DNotFoundError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
/**
|
||||
* Ошибка, обозначающая, что объект уже присутствует в контейнере.
|
||||
*/
|
||||
public class ObjectExistsError extends Engine3DError {
|
||||
|
||||
/**
|
||||
* Экземпляр или идентификатор объекта, который уже присутствует в контейнере
|
||||
*/
|
||||
public var object:Object;
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param message описание ошибки
|
||||
* @param object объект, который уже присутствует в контейнере
|
||||
* @param source объект, вызвавший ошибку
|
||||
*/
|
||||
public function ObjectExistsError(message:String = "", object:Object = null, source:Object = null) {
|
||||
super(message, source);
|
||||
this.object = object;
|
||||
this.name = "ObjectExistsError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
/**
|
||||
* Необходимый объект не был найден в контейнере.
|
||||
*/
|
||||
public class ObjectNotFoundError extends Engine3DError {
|
||||
|
||||
/**
|
||||
* Объект, который отсутствует в контейнере.
|
||||
*/
|
||||
public var object:Object;
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param message описание ошибки
|
||||
* @param object отсутствующий объект
|
||||
* @param source объект, вызвавший ошибку
|
||||
*/
|
||||
public function ObjectNotFoundError(message:String = "", object:Object = null, source:Object = null) {
|
||||
super(message, source);
|
||||
this.object = object;
|
||||
this.name = "ObjectNotFoundError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.engine3d.core.Mesh;
|
||||
import alternativa.utils.TextUtils;
|
||||
|
||||
/**
|
||||
* Ошибка, обозначающая, что поверхность уже присутствует в контейнере.
|
||||
*/
|
||||
public class SurfaceExistsError extends ObjectExistsError {
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param surface поверхность, которая уже присутствует в контейнере
|
||||
* @param mesh объект, вызвавший ошибку
|
||||
*/
|
||||
public function SurfaceExistsError(surface:Object = null, mesh:Mesh = null) {
|
||||
super(TextUtils.insertVars("Mesh %1. Surface with ID '%2' already exists.", mesh, surface), surface, mesh);
|
||||
this.name = "SurfaceExistsError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.engine3d.core.Mesh;
|
||||
import alternativa.engine3d.core.Surface;
|
||||
import alternativa.utils.TextUtils;
|
||||
|
||||
/**
|
||||
* Ошибка, обозначающая, что поверхность не найдена в контейнере.
|
||||
*/
|
||||
public class SurfaceNotFoundError extends ObjectNotFoundError {
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param surface поверхность, которая отсутствует в объекте
|
||||
* @param mesh объект, который вызвал ошибку
|
||||
*/
|
||||
public function SurfaceNotFoundError(surface:Object = null, mesh:Mesh = null) {
|
||||
if (mesh == null) {
|
||||
|
||||
}
|
||||
if (surface is Surface) {
|
||||
message = "Mesh %1. Surface %2 not found.";
|
||||
} else {
|
||||
message = "Mesh %1. Surface with ID '%2' not found.";
|
||||
}
|
||||
super(TextUtils.insertVars(message, mesh, surface), surface, mesh);
|
||||
this.name = "SurfaceNotFoundError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.engine3d.core.Mesh;
|
||||
import alternativa.utils.TextUtils;
|
||||
|
||||
/**
|
||||
* Ошибка, обозначающая, что вершина уже содержится в объекте.
|
||||
*/
|
||||
public class VertexExistsError extends ObjectExistsError {
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param vertex вершина, которая уже есть в объекте
|
||||
* @param mesh объект, вызвавший ошибку
|
||||
*/
|
||||
public function VertexExistsError(vertex:Object = null, mesh:Mesh = null) {
|
||||
super(TextUtils.insertVars("Mesh %1. Vertex with ID '%2' already exists.", mesh, vertex), vertex, mesh);
|
||||
this.name = "VertexExistsError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.engine3d.core.Mesh;
|
||||
import alternativa.engine3d.core.Vertex;
|
||||
import alternativa.utils.TextUtils;
|
||||
|
||||
/**
|
||||
* Ошибка, обозначающая, что вершина не найдена в объекте.
|
||||
*/
|
||||
public class VertexNotFoundError extends ObjectNotFoundError {
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param vertex вершина, которая не найдена в объекте
|
||||
* @param mesh объект, вызвавший ошибку
|
||||
*/
|
||||
public function VertexNotFoundError(vertex:Object = null, mesh:Mesh = null) {
|
||||
if (vertex is Vertex) {
|
||||
message = "Mesh %1. Vertex %2 not found.";
|
||||
} else {
|
||||
message = "Mesh %1. Vertex with ID '%2' not found.";
|
||||
}
|
||||
super(TextUtils.insertVars(message, mesh, vertex), vertex, mesh);
|
||||
this.name = "VertexNotFoundError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.utils.TextUtils;
|
||||
|
||||
/**
|
||||
* Базовый класс для ошибок 3d-engine.
|
||||
*/
|
||||
public class Engine3DError extends Error {
|
||||
|
||||
/**
|
||||
* Источник ошибки - объект в котором произошла ошибка.
|
||||
*/
|
||||
public var source:Object;
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param message описание ошибки
|
||||
* @param source источник ошибки
|
||||
*/
|
||||
public function Engine3DError(message:String = "", source:Object = null) {
|
||||
super(message);
|
||||
this.source = source;
|
||||
this.name = "Engine3DError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.engine3d.core.Face;
|
||||
import alternativa.engine3d.core.Mesh;
|
||||
import alternativa.utils.TextUtils;
|
||||
import alternativa.engine3d.core.Surface;
|
||||
|
||||
/**
|
||||
* Ошибка, возникающая при попытке добавить в какой-либо объект грань, уже содержащуюся в данном объекте.
|
||||
*/
|
||||
public class FaceExistsError extends ObjectExistsError {
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param face экземпляр или идентификатор грани, которая уже содержится в объекте
|
||||
* @param source источник ошибки
|
||||
*/
|
||||
public function FaceExistsError(face:Object = null, source:Object = null) {
|
||||
var message:String;
|
||||
if (source is Mesh) {
|
||||
message = "Mesh ";
|
||||
} else if (source is Surface) {
|
||||
message = "Surface ";
|
||||
}
|
||||
if (face is Face) {
|
||||
message += "%1. Face %2 already exists.";
|
||||
} else {
|
||||
message += "%1. Face with ID '%2' already exists.";
|
||||
}
|
||||
super(TextUtils.insertVars(message, source, face), face, source);
|
||||
this.name = "FaceExistsError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.engine3d.core.Mesh;
|
||||
import alternativa.utils.TextUtils;
|
||||
|
||||
/**
|
||||
* Ошибка, обозначающая недостаточное количество вершин для создания грани.
|
||||
* Для создания грани должно быть указано не менее трех вершин.
|
||||
*/
|
||||
public class FaceNeedMoreVerticesError extends Engine3DError {
|
||||
|
||||
/**
|
||||
* Количество переданных для создания грани вершин
|
||||
*/
|
||||
public var count:uint;
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param mesh объект, в котором произошла ошибка
|
||||
* @param count количество вершин, переданное для создания грани
|
||||
*/
|
||||
public function FaceNeedMoreVerticesError(mesh:Mesh = null, count:uint = 0) {
|
||||
super(TextUtils.insertVars("Mesh %1. %2 vertices not enough for face creation.", mesh, count), mesh);
|
||||
this.count = count;
|
||||
this.name = "FaceNeedMoreVerticesError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.engine3d.core.Face;
|
||||
import alternativa.engine3d.core.Mesh;
|
||||
import alternativa.utils.TextUtils;
|
||||
|
||||
/**
|
||||
* Ошибка, возникающая, если грань не найдена в объекте.
|
||||
*/
|
||||
public class FaceNotFoundError extends ObjectNotFoundError {
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param face экземпляр или идентификатор грани
|
||||
* @param source объект, в котором произошла ошибка
|
||||
*/
|
||||
public function FaceNotFoundError(face:Object = null, source:Object = null) {
|
||||
var message:String;
|
||||
if (source is Mesh) {
|
||||
message = "Mesh ";
|
||||
} else {
|
||||
message = "Surface ";
|
||||
}
|
||||
if (face is Face) {
|
||||
message += "%1. Face %2 not found.";
|
||||
} else {
|
||||
message += "%1. Face with ID '%2' not found.";
|
||||
}
|
||||
super(TextUtils.insertVars(message, source, face), face, source);
|
||||
this.name = "FaceNotFoundError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package alternativa.engine3d.errors {
|
||||
import alternativa.utils.TextUtils;
|
||||
import alternativa.engine3d.core.Mesh;
|
||||
import alternativa.engine3d.core.Surface;
|
||||
|
||||
|
||||
/**
|
||||
* Ошибка, обозначающая, что идентификатор зарезервирован и не может быть использован.
|
||||
*/
|
||||
public class InvalidIDError extends Engine3DError {
|
||||
/**
|
||||
* Зарезервированный идентификатор
|
||||
*/
|
||||
public var id:Object;
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param id идентификатор
|
||||
* @param source объект, в котором произошла ошибка
|
||||
*/
|
||||
public function InvalidIDError(id:Object = null, source:Object = null) {
|
||||
var message:String;
|
||||
if (source is Mesh) {
|
||||
message = "Mesh %2. ";
|
||||
} else if (source is Surface) {
|
||||
message = "Surface %2. ";
|
||||
}
|
||||
super(TextUtils.insertVars(message + "ID %1 is reserved and cannot be used", [id, source]), source);
|
||||
this.id = id;
|
||||
this.name = "InvalidIDError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.utils.TextUtils;
|
||||
|
||||
/**
|
||||
* Ошибка, связанная с нарушением иерархии объектов сцены.
|
||||
*/
|
||||
public class Object3DHierarchyError extends ObjectNotFoundError
|
||||
{
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param object объект, нарушающий иерархию
|
||||
* @param source источник ошибки
|
||||
*/
|
||||
public function Object3DHierarchyError(object:Object3D = null, source:Object3D = null) {
|
||||
super(TextUtils.insertVars("Object3D %1. Object %2 cannot be added", source, object), object, source);
|
||||
this.name = "Object3DHierarchyError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.utils.TextUtils;
|
||||
|
||||
/**
|
||||
* Ошибка, возникающая, когда объект сцены не был найден в списке связанных с необходимым объектом сцены.
|
||||
*/
|
||||
public class Object3DNotFoundError extends ObjectNotFoundError {
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param object ненайденный объект сцены
|
||||
* @param source объект сцены, в котором произошла ошибка
|
||||
*/
|
||||
public function Object3DNotFoundError(object:Object3D = null, source:Object3D = null) {
|
||||
super(TextUtils.insertVars("Object3D %1. Object %2 not in child list", source, object), object, source);
|
||||
this.name = "Object3DNotFoundError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
/**
|
||||
* Ошибка, обозначающая, что объект уже присутствует в контейнере.
|
||||
*/
|
||||
public class ObjectExistsError extends Engine3DError {
|
||||
|
||||
/**
|
||||
* Экземпляр или идентификатор объекта, который уже присутствует в контейнере
|
||||
*/
|
||||
public var object:Object;
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param message описание ошибки
|
||||
* @param object объект, который уже присутствует в контейнере
|
||||
* @param source объект, вызвавший ошибку
|
||||
*/
|
||||
public function ObjectExistsError(message:String = "", object:Object = null, source:Object = null) {
|
||||
super(message, source);
|
||||
this.object = object;
|
||||
this.name = "ObjectExistsError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
/**
|
||||
* Необходимый объект не был найден в контейнере.
|
||||
*/
|
||||
public class ObjectNotFoundError extends Engine3DError {
|
||||
|
||||
/**
|
||||
* Объект, который отсутствует в контейнере.
|
||||
*/
|
||||
public var object:Object;
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param message описание ошибки
|
||||
* @param object отсутствующий объект
|
||||
* @param source объект, вызвавший ошибку
|
||||
*/
|
||||
public function ObjectNotFoundError(message:String = "", object:Object = null, source:Object = null) {
|
||||
super(message, source);
|
||||
this.object = object;
|
||||
this.name = "ObjectNotFoundError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.engine3d.core.Mesh;
|
||||
import alternativa.utils.TextUtils;
|
||||
|
||||
/**
|
||||
* Ошибка, обозначающая, что поверхность уже присутствует в контейнере.
|
||||
*/
|
||||
public class SurfaceExistsError extends ObjectExistsError {
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param surface поверхность, которая уже присутствует в контейнере
|
||||
* @param mesh объект, вызвавший ошибку
|
||||
*/
|
||||
public function SurfaceExistsError(surface:Object = null, mesh:Mesh = null) {
|
||||
super(TextUtils.insertVars("Mesh %1. Surface with ID '%2' already exists.", mesh, surface), surface, mesh);
|
||||
this.name = "SurfaceExistsError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.engine3d.core.Mesh;
|
||||
import alternativa.engine3d.core.Surface;
|
||||
import alternativa.utils.TextUtils;
|
||||
|
||||
/**
|
||||
* Ошибка, обозначающая, что поверхность не найдена в контейнере.
|
||||
*/
|
||||
public class SurfaceNotFoundError extends ObjectNotFoundError {
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param surface поверхность, которая отсутствует в объекте
|
||||
* @param mesh объект, который вызвал ошибку
|
||||
*/
|
||||
public function SurfaceNotFoundError(surface:Object = null, mesh:Mesh = null) {
|
||||
if (mesh == null) {
|
||||
|
||||
}
|
||||
if (surface is Surface) {
|
||||
message = "Mesh %1. Surface %2 not found.";
|
||||
} else {
|
||||
message = "Mesh %1. Surface with ID '%2' not found.";
|
||||
}
|
||||
super(TextUtils.insertVars(message, mesh, surface), surface, mesh);
|
||||
this.name = "SurfaceNotFoundError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.engine3d.core.Mesh;
|
||||
import alternativa.utils.TextUtils;
|
||||
|
||||
/**
|
||||
* Ошибка, обозначающая, что вершина уже содержится в объекте.
|
||||
*/
|
||||
public class VertexExistsError extends ObjectExistsError {
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param vertex вершина, которая уже есть в объекте
|
||||
* @param mesh объект, вызвавший ошибку
|
||||
*/
|
||||
public function VertexExistsError(vertex:Object = null, mesh:Mesh = null) {
|
||||
super(TextUtils.insertVars("Mesh %1. Vertex with ID '%2' already exists.", mesh, vertex), vertex, mesh);
|
||||
this.name = "VertexExistsError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package alternativa.engine3d.errors {
|
||||
|
||||
import alternativa.engine3d.core.Mesh;
|
||||
import alternativa.engine3d.core.Vertex;
|
||||
import alternativa.utils.TextUtils;
|
||||
|
||||
/**
|
||||
* Ошибка, обозначающая, что вершина не найдена в объекте.
|
||||
*/
|
||||
public class VertexNotFoundError extends ObjectNotFoundError {
|
||||
|
||||
/**
|
||||
* Создание экземпляра класса.
|
||||
*
|
||||
* @param vertex вершина, которая не найдена в объекте
|
||||
* @param mesh объект, вызвавший ошибку
|
||||
*/
|
||||
public function VertexNotFoundError(vertex:Object = null, mesh:Mesh = null) {
|
||||
if (vertex is Vertex) {
|
||||
message = "Mesh %1. Vertex %2 not found.";
|
||||
} else {
|
||||
message = "Mesh %1. Vertex with ID '%2' not found.";
|
||||
}
|
||||
super(TextUtils.insertVars(message, mesh, vertex), vertex, mesh);
|
||||
this.name = "VertexNotFoundError";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 98
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/loaders
|
||||
END
|
||||
Loader3DS.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 111
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/loaders/Loader3DS.as
|
||||
END
|
||||
@@ -0,0 +1,40 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/loaders
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
Loader3DS.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:34:22.000000Z
|
||||
4935b722b934fd10c9371a4a4d9c41a5
|
||||
2008-08-25T13:14:49.962000Z
|
||||
161
|
||||
int
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
8
|
||||
@@ -0,0 +1,974 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.engine3d.core.Face;
|
||||
import alternativa.engine3d.core.Mesh;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.core.Surface;
|
||||
import alternativa.engine3d.core.Vertex;
|
||||
import alternativa.engine3d.materials.FillMaterial;
|
||||
import alternativa.engine3d.materials.TextureMaterial;
|
||||
import alternativa.engine3d.materials.TextureMaterialPrecision;
|
||||
import alternativa.engine3d.materials.WireMaterial;
|
||||
import alternativa.types.Matrix3D;
|
||||
import alternativa.types.Point3D;
|
||||
import alternativa.types.Texture;
|
||||
import alternativa.utils.ColorUtils;
|
||||
import alternativa.utils.MathUtils;
|
||||
|
||||
import flash.display.Bitmap;
|
||||
import flash.display.BitmapData;
|
||||
import flash.display.BlendMode;
|
||||
import flash.display.Loader;
|
||||
import flash.errors.IOError;
|
||||
import flash.events.Event;
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Point;
|
||||
import flash.net.URLLoader;
|
||||
import flash.net.URLLoaderDataFormat;
|
||||
import flash.net.URLRequest;
|
||||
import flash.system.LoaderContext;
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Endian;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Загрузчик моделей в формате 3DS.
|
||||
*/
|
||||
public class Loader3DS extends EventDispatcher {
|
||||
|
||||
/**
|
||||
* Дюймы
|
||||
*/
|
||||
public static const INCHES:Number = 1;
|
||||
|
||||
/**
|
||||
* Футы
|
||||
*/
|
||||
public static const FEET:Number = 0.0833333334;
|
||||
/**
|
||||
* Мили
|
||||
*/
|
||||
public static const MILES:Number = 0.0000157828;
|
||||
/**
|
||||
* Миллиметры
|
||||
*/
|
||||
public static const MILLIMETERS:Number = 25.4000000259;
|
||||
/**
|
||||
* Сантиметры
|
||||
*/
|
||||
public static const CENTIMETERS:Number = 2.5400000025;
|
||||
/**
|
||||
* Метры
|
||||
*/
|
||||
public static const METERS:Number = 0.0254;
|
||||
/**
|
||||
* Километры
|
||||
*/
|
||||
public static const KILOMETERS:Number = 0.0000254;
|
||||
|
||||
private static var stub:BitmapData;
|
||||
|
||||
private var _content:Object3D;
|
||||
private var version:uint;
|
||||
private var objectDatas:Array;
|
||||
private var animationDatas:Array;
|
||||
private var materialDatas:Array;
|
||||
private var bitmaps:Array;
|
||||
|
||||
private var urlLoader:URLLoader;
|
||||
private var data:ByteArray;
|
||||
|
||||
private var counter:uint;
|
||||
private var sequence:Array;
|
||||
private var loader:Loader;
|
||||
private var context:LoaderContext;
|
||||
private var path:String;
|
||||
|
||||
/**
|
||||
* Повтор текстуры при заливке
|
||||
*/
|
||||
public var repeat:Boolean = true;
|
||||
/**
|
||||
* Сглаживание текстур при масштабировании
|
||||
*/
|
||||
public var smooth:Boolean = false;
|
||||
/**
|
||||
* Режим наложения цветов
|
||||
*/
|
||||
public var blendMode:String = BlendMode.NORMAL;
|
||||
/**
|
||||
* Точность перспективной коррекции
|
||||
*/
|
||||
public var precision:Number = TextureMaterialPrecision.MEDIUM;
|
||||
|
||||
/**
|
||||
* Определяет в какие единицы измерения перевести координаты модели.
|
||||
* Может быть любым значением из констант Loader3DS
|
||||
* <table>
|
||||
* <tr><td><code>INCHES</code></td><td>переводить в дюймы</td></tr>
|
||||
* <tr><td><code>FEET</code></td><td>переводить в футы</td></tr>
|
||||
* <tr><td><code>MILES</code></td><td>переводить в мили</td></tr>
|
||||
* <tr><td><code>MILLIMETERS</code></td><td>переводить в миллиметры</td></tr>
|
||||
* <tr><td><code>CENTIMETERS</code></td><td>переводить в сантиметры</td></tr>
|
||||
* <tr><td><code>METERS</code></td><td>переводить в метры</td></tr>
|
||||
* <tr><td><code>KILOMETERS</code></td><td>переводить в километры</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
public var units:Number = INCHES;
|
||||
/**
|
||||
* Уровень мобильности
|
||||
*/
|
||||
public var mobility:int = 0;
|
||||
|
||||
/**
|
||||
* Загрузка модели.
|
||||
*
|
||||
* @param url адрес, откуда загружается модель
|
||||
* @param context
|
||||
*/
|
||||
public function load(url:String, context:LoaderContext = null):void {
|
||||
path = url.substring(0, url.lastIndexOf("/") + 1);
|
||||
this.context = context;
|
||||
|
||||
urlLoader = new URLLoader();
|
||||
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
|
||||
urlLoader.load(new URLRequest(url));
|
||||
urlLoader.addEventListener(Event.COMPLETE, on3DSLoad);
|
||||
urlLoader.addEventListener(IOErrorEvent.IO_ERROR, on3DSError);
|
||||
urlLoader.addEventListener(IOErrorEvent.NETWORK_ERROR, on3DSError);
|
||||
urlLoader.addEventListener(IOErrorEvent.VERIFY_ERROR, on3DSError);
|
||||
|
||||
// Очистка
|
||||
_content = null;
|
||||
version = 0;
|
||||
objectDatas = null;
|
||||
animationDatas = null;
|
||||
materialDatas = null;
|
||||
bitmaps = null;
|
||||
sequence = null;
|
||||
if (loader != null) {
|
||||
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loadNextBitmap);
|
||||
loader.close();
|
||||
loader = null;
|
||||
}
|
||||
}
|
||||
|
||||
private function on3DSLoad(e:Event):void {
|
||||
data = urlLoader.data;
|
||||
data.endian = Endian.LITTLE_ENDIAN;
|
||||
parse3DSChunk(0, data.bytesAvailable);
|
||||
|
||||
urlLoader.removeEventListener(Event.COMPLETE, on3DSLoad);
|
||||
urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, on3DSError);
|
||||
urlLoader.removeEventListener(IOErrorEvent.NETWORK_ERROR, on3DSError);
|
||||
urlLoader.removeEventListener(IOErrorEvent.VERIFY_ERROR, on3DSError);
|
||||
urlLoader = null;
|
||||
}
|
||||
|
||||
private function on3DSError(e:Event):void {
|
||||
_content = null;
|
||||
throw new IOError(IOErrorEvent(e).text);
|
||||
|
||||
urlLoader.removeEventListener(Event.COMPLETE, on3DSLoad);
|
||||
urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, on3DSError);
|
||||
urlLoader.removeEventListener(IOErrorEvent.NETWORK_ERROR, on3DSError);
|
||||
urlLoader.removeEventListener(IOErrorEvent.VERIFY_ERROR, on3DSError);
|
||||
}
|
||||
|
||||
private function loadBitmaps():void {
|
||||
if (bitmaps != null) {
|
||||
counter = 0;
|
||||
sequence = new Array();
|
||||
for (var filename:String in bitmaps) {
|
||||
sequence.push(filename);
|
||||
}
|
||||
loader = new Loader();
|
||||
loader.load(new URLRequest(path + sequence[counter]), context);
|
||||
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadNextBitmap);
|
||||
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadNextBitmap);
|
||||
loader.contentLoaderInfo.addEventListener(IOErrorEvent.NETWORK_ERROR, loadNextBitmap);
|
||||
loader.contentLoaderInfo.addEventListener(IOErrorEvent.VERIFY_ERROR, loadNextBitmap);
|
||||
} else {
|
||||
buildContent();
|
||||
}
|
||||
}
|
||||
|
||||
private function loadNextBitmap(e:Event = null):void {
|
||||
if (!(e is IOErrorEvent)) {
|
||||
bitmaps[sequence[counter]] = Bitmap(loader.content).bitmapData;
|
||||
} else {
|
||||
if (stub == null) {
|
||||
var size:uint = 10;
|
||||
stub = new BitmapData(size, size, false, 0);
|
||||
for (var i:uint = 0; i < size; i++) {
|
||||
for (var j:uint = 0; j < size; j+=2) {
|
||||
stub.setPixel((i % 2) ? j : (j+1), i, 0xFF00FF);
|
||||
}
|
||||
}
|
||||
}
|
||||
bitmaps[sequence[counter]] = stub;
|
||||
}
|
||||
counter++;
|
||||
if (counter < sequence.length) {
|
||||
loader.load(new URLRequest(path + sequence[counter]), context);
|
||||
} else {
|
||||
buildContent();
|
||||
}
|
||||
if (e is IOErrorEvent) {
|
||||
throw new IOError(IOErrorEvent(e).text);
|
||||
}
|
||||
}
|
||||
|
||||
private function buildContent():void {
|
||||
var i:uint;
|
||||
var length:uint;
|
||||
|
||||
// Формируем связи объектов
|
||||
_content = new Object3D();
|
||||
|
||||
// Создаём материалы
|
||||
var materialData:MaterialData;
|
||||
for (var materialName:String in materialDatas) {
|
||||
materialData = materialDatas[materialName];
|
||||
var mapData:MapData = materialData.diffuseMap;
|
||||
var materialMatrix:Matrix = new Matrix();
|
||||
if (mapData != null) {
|
||||
var rot:Number = MathUtils.toRadian(mapData.rotation);
|
||||
var rotSin:Number = Math.sin(rot);
|
||||
var rotCos:Number = Math.cos(rot);
|
||||
materialMatrix.translate(-mapData.offsetU, mapData.offsetV);
|
||||
materialMatrix.translate(-0.5, -0.5);
|
||||
materialMatrix.rotate(-rot);
|
||||
materialMatrix.scale(mapData.scaleU, mapData.scaleV);
|
||||
materialMatrix.translate(0.5, 0.5);
|
||||
}
|
||||
materialData.matrix = materialMatrix;
|
||||
}
|
||||
|
||||
// Если есть данные об анимации и иерархии объектов
|
||||
var objectName:String;
|
||||
var objectData:ObjectData;
|
||||
var mesh:Mesh;
|
||||
if (animationDatas != null) {
|
||||
if (objectDatas != null) {
|
||||
length = animationDatas.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var animationData:AnimationData = animationDatas[i];
|
||||
objectName = animationData.objectName;
|
||||
objectData = objectDatas[objectName];
|
||||
if (objectData != null && objectData.vertices != null) {
|
||||
// Меш
|
||||
mesh = new Mesh(objectName);
|
||||
animationData.object = mesh;
|
||||
buildObject(animationData);
|
||||
buildMesh(mesh, objectData, animationData);
|
||||
} else {
|
||||
var object:Object3D = new Object3D(objectName);
|
||||
animationData.object = object;
|
||||
buildObject(animationData);
|
||||
}
|
||||
}
|
||||
buildHierarchy(_content, 0, length - 1);
|
||||
}
|
||||
} else {
|
||||
for (objectName in objectDatas) {
|
||||
objectData = objectDatas[objectName];
|
||||
if (objectData.vertices != null) {
|
||||
// Меш
|
||||
mesh = new Mesh(objectName);
|
||||
buildMesh(mesh, objectData, null);
|
||||
_content.addChild(mesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Рассылаем событие о завершении
|
||||
dispatchEvent(new Event(Event.COMPLETE));
|
||||
}
|
||||
|
||||
private function buildObject(animationData:AnimationData):void {
|
||||
var object:Object3D = animationData.object;
|
||||
if (animationData.position != null) {
|
||||
object.x = animationData.position.x;
|
||||
object.y = animationData.position.y;
|
||||
object.z = animationData.position.z;
|
||||
}
|
||||
if (animationData.rotation != null) {
|
||||
object.rotationX = animationData.rotation.x;
|
||||
object.rotationY = animationData.rotation.y;
|
||||
object.rotationZ = animationData.rotation.z;
|
||||
}
|
||||
if (animationData.scale != null) {
|
||||
object.scaleX = animationData.scale.x;
|
||||
object.scaleY = animationData.scale.y;
|
||||
object.scaleZ = animationData.scale.z;
|
||||
}
|
||||
object.mobility = mobility;
|
||||
}
|
||||
|
||||
private function buildMesh(mesh:Mesh, objectData:ObjectData, animationData:AnimationData):void {
|
||||
// Добавляем вершины
|
||||
var i:uint;
|
||||
var length:uint = objectData.vertices.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var vertexData:Point3D = objectData.vertices[i];
|
||||
objectData.vertices[i] = mesh.addVertex(vertexData.x, vertexData.y, vertexData.z, i);
|
||||
}
|
||||
|
||||
// Коррекция вершин
|
||||
if (animationData != null) {
|
||||
// Инвертируем матрицу
|
||||
objectData.matrix.invert();
|
||||
|
||||
// Вычитаем точку привязки из смещения матрицы
|
||||
objectData.matrix.d -= animationData.pivot.x;
|
||||
objectData.matrix.h -= animationData.pivot.y;
|
||||
objectData.matrix.l -= animationData.pivot.z;
|
||||
|
||||
// Трансформируем вершины
|
||||
for (var key:* in mesh._vertices) {
|
||||
var vertex:Vertex = mesh._vertices[key];
|
||||
vertex._coords.transform(objectData.matrix);
|
||||
vertex._coords.multiply(units);
|
||||
}
|
||||
}
|
||||
|
||||
// Добавляем грани
|
||||
length = objectData.faces.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var faceData:FaceData = objectData.faces[i];
|
||||
mesh.addFace([objectData.vertices[faceData.a], objectData.vertices[faceData.b], objectData.vertices[faceData.c]], i);
|
||||
}
|
||||
// Добавляем поверхности
|
||||
if (objectData.surfaces != null) {
|
||||
for (var surfaceId:String in objectData.surfaces) {
|
||||
var materialData:MaterialData = materialDatas[surfaceId];
|
||||
var surfaceData:SurfaceData = objectData.surfaces[surfaceId];
|
||||
var surface:Surface = mesh.addSurface(surfaceData.faces, surfaceId);
|
||||
if (materialData.diffuseMap != null || materialData.normalMap != null) {
|
||||
surface.material = new TextureMaterial(new Texture(bitmaps[materialData.diffuseMap.filename], materialData.diffuseMap.filename), 1 - materialData.transparency/100, repeat, smooth, blendMode, precision);
|
||||
length = surfaceData.faces.length;
|
||||
if (objectData.uvs != null) {
|
||||
for (i = 0; i < length; i++) {
|
||||
var id:uint = surfaceData.faces[i];
|
||||
var fd:FaceData = objectData.faces[id];
|
||||
var face:Face = mesh.getFaceById(id);
|
||||
var aUV:Point = objectData.uvs[fd.a];
|
||||
var bUV:Point = objectData.uvs[fd.b];
|
||||
var cUV:Point = objectData.uvs[fd.c];
|
||||
if (aUV != null && bUV != null && cUV != null && ((bUV.x - aUV.x)*(cUV.y - aUV.y) - (bUV.y - aUV.y)*(cUV.x - aUV.x) != 0)) {
|
||||
face.aUV = materialData.matrix.transformPoint(aUV);
|
||||
face.bUV = materialData.matrix.transformPoint(bUV);
|
||||
face.cUV = materialData.matrix.transformPoint(cUV);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
surface.material = new FillMaterial(materialDatas[surfaceId].color, 1 - materialData.transparency/100);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Поверхность по умолчанию
|
||||
var defaultSurface:Surface = mesh.addSurface();
|
||||
// Добавляем грани
|
||||
for (var faceId:String in mesh._faces) {
|
||||
defaultSurface.addFace(mesh._faces[faceId]);
|
||||
}
|
||||
defaultSurface.material = new WireMaterial(0);
|
||||
}
|
||||
}
|
||||
|
||||
private function buildHierarchy(parent:Object3D, begin:uint, end:uint):void {
|
||||
if (begin <= end) {
|
||||
var animation:AnimationData = animationDatas[begin];
|
||||
var object:Object3D = animation.object;
|
||||
parent.addChild(object);
|
||||
|
||||
var parentIndex:uint = animation.parentIndex;
|
||||
for (var i:uint = begin + 1; i <= end; i++) {
|
||||
animation = animationDatas[i];
|
||||
if (parentIndex == animation.parentIndex) {
|
||||
buildHierarchy(object, begin + 1, i - 1);
|
||||
buildHierarchy(parent, i, end);
|
||||
return;
|
||||
}
|
||||
}
|
||||
buildHierarchy(object, begin + 1, end);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function parse3DSChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Главный
|
||||
case 0x4D4D:
|
||||
parseMainChunk(dataIndex, dataLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parse3DSChunk(index + chunkLength, length - chunkLength);
|
||||
} else {
|
||||
// Загрузка битмап
|
||||
loadBitmaps();
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMainChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Версия
|
||||
case 0x0002:
|
||||
parseVersion(dataIndex);
|
||||
break;
|
||||
// 3D-сцена
|
||||
case 0x3D3D:
|
||||
parse3DChunk(dataIndex, dataLength);
|
||||
break;
|
||||
// Анимация
|
||||
case 0xB000:
|
||||
parseAnimationChunk(dataIndex, dataLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parseMainChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseVersion(index:uint):void {
|
||||
data.position = index;
|
||||
version = data.readUnsignedInt();
|
||||
}
|
||||
|
||||
private function parse3DChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Материал
|
||||
case 0xAFFF:
|
||||
// Парсим материал
|
||||
var material:MaterialData = new MaterialData();
|
||||
parseMaterialChunk(material, dataIndex, dataLength);
|
||||
break;
|
||||
// Объект
|
||||
case 0x4000:
|
||||
// Создаём данные объекта
|
||||
var object:ObjectData = new ObjectData();
|
||||
var objectLength:uint = parseObject(object, dataIndex);
|
||||
// Парсим объект
|
||||
parseObjectChunk(object, dataIndex + objectLength, dataLength - objectLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parse3DChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMaterialChunk(material:MaterialData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Имя материала
|
||||
case 0xA000:
|
||||
parseMaterialName(material, dataIndex);
|
||||
break;
|
||||
// Ambient color
|
||||
case 0xA010:
|
||||
break;
|
||||
// Diffuse color
|
||||
case 0xA020:
|
||||
data.position = dataIndex + 6;
|
||||
material.color = ColorUtils.rgb(data.readUnsignedByte(), data.readUnsignedByte(), data.readUnsignedByte());
|
||||
break;
|
||||
// Specular color
|
||||
case 0xA030:
|
||||
break;
|
||||
// Shininess percent
|
||||
case 0xA040:
|
||||
data.position = dataIndex + 6;
|
||||
material.glossiness = data.readUnsignedShort();
|
||||
break;
|
||||
// Shininess strength percent
|
||||
case 0xA041:
|
||||
data.position = dataIndex + 6;
|
||||
material.specular = data.readUnsignedShort();
|
||||
break;
|
||||
// Transperensy
|
||||
case 0xA050:
|
||||
data.position = dataIndex + 6;
|
||||
material.transparency = data.readUnsignedShort();
|
||||
break;
|
||||
// Texture map 1
|
||||
case 0xA200:
|
||||
material.diffuseMap = new MapData();
|
||||
parseMapChunk(material.diffuseMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Texture map 2
|
||||
case 0xA33A:
|
||||
break;
|
||||
// Opacity map
|
||||
case 0xA210:
|
||||
break;
|
||||
// Bump map
|
||||
case 0xA230:
|
||||
material.normalMap = new MapData();
|
||||
parseMapChunk(material.normalMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Shininess map
|
||||
case 0xA33C:
|
||||
break;
|
||||
// Specular map
|
||||
case 0xA204:
|
||||
break;
|
||||
// Self-illumination map
|
||||
case 0xA33D:
|
||||
break;
|
||||
// Reflection map
|
||||
case 0xA220:
|
||||
break;
|
||||
}
|
||||
|
||||
parseMaterialChunk(material, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMaterialName(material:MaterialData, index:uint):void {
|
||||
// Создаём список материалов, если надо
|
||||
if (materialDatas == null) {
|
||||
materialDatas = new Array();
|
||||
}
|
||||
// Получаем название материала
|
||||
material.name = getString(index);
|
||||
// Помещаем данные материала в список
|
||||
materialDatas[material.name] = material;
|
||||
}
|
||||
|
||||
private function parseMapChunk(map:MapData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Имя файла
|
||||
case 0xA300:
|
||||
map.filename = getString(dataIndex).toLowerCase();
|
||||
if (bitmaps == null) {
|
||||
bitmaps = new Array();
|
||||
}
|
||||
bitmaps[map.filename] = null;
|
||||
break;
|
||||
// Масштаб по U
|
||||
case 0xA354:
|
||||
data.position = dataIndex;
|
||||
map.scaleU = data.readFloat();
|
||||
break;
|
||||
// Масштаб по V
|
||||
case 0xA356:
|
||||
data.position = dataIndex;
|
||||
map.scaleV = data.readFloat();
|
||||
break;
|
||||
// Смещение по U
|
||||
case 0xA358:
|
||||
data.position = dataIndex;
|
||||
map.offsetU = data.readFloat();
|
||||
break;
|
||||
// Смещение по V
|
||||
case 0xA35A:
|
||||
data.position = dataIndex;
|
||||
map.offsetV = data.readFloat();
|
||||
break;
|
||||
// Угол поворота
|
||||
case 0xA35C:
|
||||
data.position = dataIndex;
|
||||
map.rotation = data.readFloat();
|
||||
break;
|
||||
}
|
||||
|
||||
parseMapChunk(map, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseObject(object:ObjectData, index:uint):uint {
|
||||
// Создаём список объектов, если надо
|
||||
if (objectDatas == null) {
|
||||
objectDatas = new Array();
|
||||
}
|
||||
// Получаем название объекта
|
||||
object.name = getString(index);
|
||||
// Помещаем данные объекта в список
|
||||
objectDatas[object.name] = object;
|
||||
return object.name.length + 1;
|
||||
}
|
||||
|
||||
private function parseObjectChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Меш
|
||||
case 0x4100:
|
||||
parseMeshChunk(object, dataIndex, dataLength);
|
||||
break;
|
||||
// Источник света
|
||||
case 0x4600:
|
||||
break;
|
||||
// Камера
|
||||
case 0x4700:
|
||||
break;
|
||||
}
|
||||
|
||||
parseObjectChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMeshChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Вершины
|
||||
case 0x4110:
|
||||
parseVertices(object, dataIndex);
|
||||
break;
|
||||
// UV
|
||||
case 0x4140:
|
||||
parseUVs(object, dataIndex);
|
||||
break;
|
||||
// Трансформация
|
||||
case 0x4160:
|
||||
parseMatrix(object, dataIndex);
|
||||
break;
|
||||
// Грани
|
||||
case 0x4120:
|
||||
var facesLength:uint = parseFaces(object, dataIndex);
|
||||
parseFacesChunk(object, dataIndex + facesLength, dataLength - facesLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parseMeshChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseVertices(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.vertices = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
object.vertices.push(new Point3D(data.readFloat(), data.readFloat(), data.readFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
private function parseUVs(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.uvs = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
object.uvs.push(new Point(data.readFloat(), data.readFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMatrix(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
object.matrix = new Matrix3D();
|
||||
object.matrix.a = data.readFloat();
|
||||
object.matrix.e = data.readFloat();
|
||||
object.matrix.i = data.readFloat();
|
||||
object.matrix.b = data.readFloat();
|
||||
object.matrix.f = data.readFloat();
|
||||
object.matrix.j = data.readFloat();
|
||||
object.matrix.c = data.readFloat();
|
||||
object.matrix.g = data.readFloat();
|
||||
object.matrix.k = data.readFloat();
|
||||
object.matrix.d = data.readFloat();
|
||||
object.matrix.h = data.readFloat();
|
||||
object.matrix.l = data.readFloat();
|
||||
}
|
||||
|
||||
private function parseFaces(object:ObjectData, index:uint):uint {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.faces = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
var face:FaceData = new FaceData();
|
||||
face.a = data.readUnsignedShort();
|
||||
face.b = data.readUnsignedShort();
|
||||
face.c = data.readUnsignedShort();
|
||||
object.faces.push(face);
|
||||
data.position += 2; // Пропускаем флаг
|
||||
}
|
||||
return 2 + num*8;
|
||||
}
|
||||
|
||||
private function parseFacesChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Поверхности
|
||||
case 0x4130:
|
||||
parseSurface(object, dataIndex);
|
||||
break;
|
||||
// Группы сглаживания
|
||||
case 0x4150:
|
||||
break;
|
||||
}
|
||||
|
||||
parseFacesChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseSurface(object:ObjectData, index:uint):void {
|
||||
// Создаём данные поверхности
|
||||
var surface:SurfaceData = new SurfaceData();
|
||||
// Создаём список поверхностей, если надо
|
||||
if (object.surfaces == null) {
|
||||
object.surfaces = new Array();
|
||||
}
|
||||
// Получаем название материала
|
||||
surface.materialName = getString(index);
|
||||
// Помещаем данные поверхности в список
|
||||
object.surfaces[surface.materialName] = surface;
|
||||
|
||||
// Получаем грани поверхности
|
||||
data.position = index + surface.materialName.length + 1;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
surface.faces = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
surface.faces.push(data.readUnsignedShort());
|
||||
}
|
||||
}
|
||||
|
||||
private function parseAnimationChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Анимация объекта
|
||||
case 0xB001:
|
||||
case 0xB002:
|
||||
case 0xB003:
|
||||
case 0xB004:
|
||||
case 0xB005:
|
||||
case 0xB006:
|
||||
case 0xB007:
|
||||
var animation:AnimationData = new AnimationData();
|
||||
if (animationDatas == null) {
|
||||
animationDatas = new Array();
|
||||
}
|
||||
animationDatas.push(animation);
|
||||
parseObjectAnimationChunk(animation, dataIndex, dataLength);
|
||||
break;
|
||||
|
||||
// Таймлайн
|
||||
case 0xB008:
|
||||
break;
|
||||
}
|
||||
|
||||
parseAnimationChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseObjectAnimationChunk(animation:AnimationData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Идентификация объекта и его связь
|
||||
case 0xB010:
|
||||
parseObjectAnimationInfo(animation, dataIndex);
|
||||
break;
|
||||
// Точка привязки объекта (pivot)
|
||||
case 0xB013:
|
||||
parseObjectAnimationPivot(animation, dataIndex);
|
||||
break;
|
||||
// Смещение объекта относительно родителя
|
||||
case 0xB020:
|
||||
parseObjectAnimationPosition(animation, dataIndex);
|
||||
break;
|
||||
// Поворот объекта относительно родителя (angle-axis)
|
||||
case 0xB021:
|
||||
parseObjectAnimationRotation(animation, dataIndex);
|
||||
break;
|
||||
// Масштабирование объекта относительно родителя
|
||||
case 0xB022:
|
||||
parseObjectAnimationScale(animation, dataIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
parseObjectAnimationChunk(animation, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseObjectAnimationInfo(animation:AnimationData, index:uint):void {
|
||||
var name:String = getString(index);
|
||||
data.position = index + name.length + 1 + 4;
|
||||
animation.objectName = name;
|
||||
animation.parentIndex = data.readUnsignedShort();
|
||||
}
|
||||
|
||||
private function parseObjectAnimationPivot(animation:AnimationData, index:uint):void {
|
||||
data.position = index;
|
||||
animation.pivot = new Point3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
private function parseObjectAnimationPosition(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
animation.position = new Point3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
private function parseObjectAnimationRotation(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
animation.rotation = getRotationFrom3DSAngleAxis(data.readFloat(), data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
private function parseObjectAnimationScale(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
animation.scale = new Point3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
public function get content():Object3D {
|
||||
return _content;
|
||||
}
|
||||
|
||||
// Считываем строку заканчивающуюся на нулевой байт
|
||||
public function getString(index:uint):String {
|
||||
data.position = index;
|
||||
var charCode:uint = data.readByte();
|
||||
var res:String = "";
|
||||
while (charCode != 0) {
|
||||
res += String.fromCharCode(charCode);
|
||||
charCode = data.readByte();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private function getRotationFrom3DSAngleAxis(angle:Number, x:Number, z:Number, y:Number):Point3D {
|
||||
var res:Point3D = new Point3D();
|
||||
var s:Number = Math.sin(angle);
|
||||
var c:Number = Math.cos(angle);
|
||||
var t:Number = 1 - c;
|
||||
var k:Number = x*y*t + z*s;
|
||||
var half:Number;
|
||||
if (k > 0.998) {
|
||||
half = angle/2;
|
||||
res.z = -2*Math.atan2(x*Math.sin(half), Math.cos(half));
|
||||
res.y = -Math.PI/2;
|
||||
res.x = 0;
|
||||
return res;
|
||||
}
|
||||
if (k < -0.998) {
|
||||
half = angle/2;
|
||||
res.z = 2*Math.atan2(x*Math.sin(half), Math.cos(half));
|
||||
res.y = Math.PI/2;
|
||||
res.x = 0;
|
||||
return res;
|
||||
}
|
||||
res.z = -Math.atan2(y*s - x*z*t, 1 - (y*y + z*z)*t);
|
||||
res.y = -Math.asin(x*y*t + z*s);
|
||||
res.x = -Math.atan2(x*s - y*z*t, 1 - (x*x + z*z)*t);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.types.Matrix3D;
|
||||
import alternativa.types.Point3D;
|
||||
|
||||
import flash.geom.Matrix;
|
||||
|
||||
class MaterialData {
|
||||
public var name:String;
|
||||
public var color:uint;
|
||||
public var specular:uint;
|
||||
public var glossiness:uint;
|
||||
public var transparency:uint;
|
||||
public var diffuseMap:MapData;
|
||||
public var normalMap:MapData;
|
||||
public var matrix:Matrix;
|
||||
}
|
||||
|
||||
class MapData {
|
||||
public var filename:String;
|
||||
public var scaleU:Number = 1;
|
||||
public var scaleV:Number = 1;
|
||||
public var offsetU:Number = 0;
|
||||
public var offsetV:Number = 0;
|
||||
public var rotation:Number = 0;
|
||||
}
|
||||
|
||||
class ObjectData {
|
||||
public var name:String;
|
||||
public var vertices:Array;
|
||||
public var uvs:Array;
|
||||
public var matrix:Matrix3D;
|
||||
public var faces:Array;
|
||||
public var surfaces:Array;
|
||||
}
|
||||
|
||||
class FaceData {
|
||||
public var a:uint;
|
||||
public var b:uint;
|
||||
public var c:uint;
|
||||
}
|
||||
|
||||
class SurfaceData {
|
||||
public var materialName:String;
|
||||
public var faces:Array;
|
||||
}
|
||||
|
||||
class AnimationData {
|
||||
public var objectName:String;
|
||||
public var object:Object3D;
|
||||
public var parentIndex:uint;
|
||||
public var pivot:Point3D;
|
||||
public var position:Point3D;
|
||||
public var rotation:Point3D;
|
||||
public var scale:Point3D;
|
||||
}
|
||||
974
Alternativa3D5/5.0.0/alternativa/engine3d/loaders/Loader3DS.as
Normal file
974
Alternativa3D5/5.0.0/alternativa/engine3d/loaders/Loader3DS.as
Normal file
@@ -0,0 +1,974 @@
|
||||
package alternativa.engine3d.loaders {
|
||||
import alternativa.engine3d.*;
|
||||
import alternativa.engine3d.core.Face;
|
||||
import alternativa.engine3d.core.Mesh;
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.engine3d.core.Surface;
|
||||
import alternativa.engine3d.core.Vertex;
|
||||
import alternativa.engine3d.materials.FillMaterial;
|
||||
import alternativa.engine3d.materials.TextureMaterial;
|
||||
import alternativa.engine3d.materials.TextureMaterialPrecision;
|
||||
import alternativa.engine3d.materials.WireMaterial;
|
||||
import alternativa.types.Matrix3D;
|
||||
import alternativa.types.Point3D;
|
||||
import alternativa.types.Texture;
|
||||
import alternativa.utils.ColorUtils;
|
||||
import alternativa.utils.MathUtils;
|
||||
|
||||
import flash.display.Bitmap;
|
||||
import flash.display.BitmapData;
|
||||
import flash.display.BlendMode;
|
||||
import flash.display.Loader;
|
||||
import flash.errors.IOError;
|
||||
import flash.events.Event;
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Point;
|
||||
import flash.net.URLLoader;
|
||||
import flash.net.URLLoaderDataFormat;
|
||||
import flash.net.URLRequest;
|
||||
import flash.system.LoaderContext;
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Endian;
|
||||
|
||||
use namespace alternativa3d;
|
||||
|
||||
/**
|
||||
* Загрузчик моделей в формате 3DS.
|
||||
*/
|
||||
public class Loader3DS extends EventDispatcher {
|
||||
|
||||
/**
|
||||
* Дюймы
|
||||
*/
|
||||
public static const INCHES:Number = 1;
|
||||
|
||||
/**
|
||||
* Футы
|
||||
*/
|
||||
public static const FEET:Number = 0.0833333334;
|
||||
/**
|
||||
* Мили
|
||||
*/
|
||||
public static const MILES:Number = 0.0000157828;
|
||||
/**
|
||||
* Миллиметры
|
||||
*/
|
||||
public static const MILLIMETERS:Number = 25.4000000259;
|
||||
/**
|
||||
* Сантиметры
|
||||
*/
|
||||
public static const CENTIMETERS:Number = 2.5400000025;
|
||||
/**
|
||||
* Метры
|
||||
*/
|
||||
public static const METERS:Number = 0.0254;
|
||||
/**
|
||||
* Километры
|
||||
*/
|
||||
public static const KILOMETERS:Number = 0.0000254;
|
||||
|
||||
private static var stub:BitmapData;
|
||||
|
||||
private var _content:Object3D;
|
||||
private var version:uint;
|
||||
private var objectDatas:Array;
|
||||
private var animationDatas:Array;
|
||||
private var materialDatas:Array;
|
||||
private var bitmaps:Array;
|
||||
|
||||
private var urlLoader:URLLoader;
|
||||
private var data:ByteArray;
|
||||
|
||||
private var counter:uint;
|
||||
private var sequence:Array;
|
||||
private var loader:Loader;
|
||||
private var context:LoaderContext;
|
||||
private var path:String;
|
||||
|
||||
/**
|
||||
* Повтор текстуры при заливке
|
||||
*/
|
||||
public var repeat:Boolean = true;
|
||||
/**
|
||||
* Сглаживание текстур при масштабировании
|
||||
*/
|
||||
public var smooth:Boolean = false;
|
||||
/**
|
||||
* Режим наложения цветов
|
||||
*/
|
||||
public var blendMode:String = BlendMode.NORMAL;
|
||||
/**
|
||||
* Точность перспективной коррекции
|
||||
*/
|
||||
public var precision:Number = TextureMaterialPrecision.MEDIUM;
|
||||
|
||||
/**
|
||||
* Определяет в какие единицы измерения перевести координаты модели.
|
||||
* Может быть любым значением из констант Loader3DS
|
||||
* <table>
|
||||
* <tr><td><code>INCHES</code></td><td>переводить в дюймы</td></tr>
|
||||
* <tr><td><code>FEET</code></td><td>переводить в футы</td></tr>
|
||||
* <tr><td><code>MILES</code></td><td>переводить в мили</td></tr>
|
||||
* <tr><td><code>MILLIMETERS</code></td><td>переводить в миллиметры</td></tr>
|
||||
* <tr><td><code>CENTIMETERS</code></td><td>переводить в сантиметры</td></tr>
|
||||
* <tr><td><code>METERS</code></td><td>переводить в метры</td></tr>
|
||||
* <tr><td><code>KILOMETERS</code></td><td>переводить в километры</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
public var units:Number = INCHES;
|
||||
/**
|
||||
* Уровень мобильности
|
||||
*/
|
||||
public var mobility:int = 0;
|
||||
|
||||
/**
|
||||
* Загрузка модели.
|
||||
*
|
||||
* @param url адрес, откуда загружается модель
|
||||
* @param context
|
||||
*/
|
||||
public function load(url:String, context:LoaderContext = null):void {
|
||||
path = url.substring(0, url.lastIndexOf("/") + 1);
|
||||
this.context = context;
|
||||
|
||||
urlLoader = new URLLoader();
|
||||
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
|
||||
urlLoader.load(new URLRequest(url));
|
||||
urlLoader.addEventListener(Event.COMPLETE, on3DSLoad);
|
||||
urlLoader.addEventListener(IOErrorEvent.IO_ERROR, on3DSError);
|
||||
urlLoader.addEventListener(IOErrorEvent.NETWORK_ERROR, on3DSError);
|
||||
urlLoader.addEventListener(IOErrorEvent.VERIFY_ERROR, on3DSError);
|
||||
|
||||
// Очистка
|
||||
_content = null;
|
||||
version = 0;
|
||||
objectDatas = null;
|
||||
animationDatas = null;
|
||||
materialDatas = null;
|
||||
bitmaps = null;
|
||||
sequence = null;
|
||||
if (loader != null) {
|
||||
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loadNextBitmap);
|
||||
loader.close();
|
||||
loader = null;
|
||||
}
|
||||
}
|
||||
|
||||
private function on3DSLoad(e:Event):void {
|
||||
data = urlLoader.data;
|
||||
data.endian = Endian.LITTLE_ENDIAN;
|
||||
parse3DSChunk(0, data.bytesAvailable);
|
||||
|
||||
urlLoader.removeEventListener(Event.COMPLETE, on3DSLoad);
|
||||
urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, on3DSError);
|
||||
urlLoader.removeEventListener(IOErrorEvent.NETWORK_ERROR, on3DSError);
|
||||
urlLoader.removeEventListener(IOErrorEvent.VERIFY_ERROR, on3DSError);
|
||||
urlLoader = null;
|
||||
}
|
||||
|
||||
private function on3DSError(e:Event):void {
|
||||
_content = null;
|
||||
throw new IOError(IOErrorEvent(e).text);
|
||||
|
||||
urlLoader.removeEventListener(Event.COMPLETE, on3DSLoad);
|
||||
urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, on3DSError);
|
||||
urlLoader.removeEventListener(IOErrorEvent.NETWORK_ERROR, on3DSError);
|
||||
urlLoader.removeEventListener(IOErrorEvent.VERIFY_ERROR, on3DSError);
|
||||
}
|
||||
|
||||
private function loadBitmaps():void {
|
||||
if (bitmaps != null) {
|
||||
counter = 0;
|
||||
sequence = new Array();
|
||||
for (var filename:String in bitmaps) {
|
||||
sequence.push(filename);
|
||||
}
|
||||
loader = new Loader();
|
||||
loader.load(new URLRequest(path + sequence[counter]), context);
|
||||
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadNextBitmap);
|
||||
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadNextBitmap);
|
||||
loader.contentLoaderInfo.addEventListener(IOErrorEvent.NETWORK_ERROR, loadNextBitmap);
|
||||
loader.contentLoaderInfo.addEventListener(IOErrorEvent.VERIFY_ERROR, loadNextBitmap);
|
||||
} else {
|
||||
buildContent();
|
||||
}
|
||||
}
|
||||
|
||||
private function loadNextBitmap(e:Event = null):void {
|
||||
if (!(e is IOErrorEvent)) {
|
||||
bitmaps[sequence[counter]] = Bitmap(loader.content).bitmapData;
|
||||
} else {
|
||||
if (stub == null) {
|
||||
var size:uint = 10;
|
||||
stub = new BitmapData(size, size, false, 0);
|
||||
for (var i:uint = 0; i < size; i++) {
|
||||
for (var j:uint = 0; j < size; j+=2) {
|
||||
stub.setPixel((i % 2) ? j : (j+1), i, 0xFF00FF);
|
||||
}
|
||||
}
|
||||
}
|
||||
bitmaps[sequence[counter]] = stub;
|
||||
}
|
||||
counter++;
|
||||
if (counter < sequence.length) {
|
||||
loader.load(new URLRequest(path + sequence[counter]), context);
|
||||
} else {
|
||||
buildContent();
|
||||
}
|
||||
if (e is IOErrorEvent) {
|
||||
throw new IOError(IOErrorEvent(e).text);
|
||||
}
|
||||
}
|
||||
|
||||
private function buildContent():void {
|
||||
var i:uint;
|
||||
var length:uint;
|
||||
|
||||
// Формируем связи объектов
|
||||
_content = new Object3D();
|
||||
|
||||
// Создаём материалы
|
||||
var materialData:MaterialData;
|
||||
for (var materialName:String in materialDatas) {
|
||||
materialData = materialDatas[materialName];
|
||||
var mapData:MapData = materialData.diffuseMap;
|
||||
var materialMatrix:Matrix = new Matrix();
|
||||
if (mapData != null) {
|
||||
var rot:Number = MathUtils.toRadian(mapData.rotation);
|
||||
var rotSin:Number = Math.sin(rot);
|
||||
var rotCos:Number = Math.cos(rot);
|
||||
materialMatrix.translate(-mapData.offsetU, mapData.offsetV);
|
||||
materialMatrix.translate(-0.5, -0.5);
|
||||
materialMatrix.rotate(-rot);
|
||||
materialMatrix.scale(mapData.scaleU, mapData.scaleV);
|
||||
materialMatrix.translate(0.5, 0.5);
|
||||
}
|
||||
materialData.matrix = materialMatrix;
|
||||
}
|
||||
|
||||
// Если есть данные об анимации и иерархии объектов
|
||||
var objectName:String;
|
||||
var objectData:ObjectData;
|
||||
var mesh:Mesh;
|
||||
if (animationDatas != null) {
|
||||
if (objectDatas != null) {
|
||||
length = animationDatas.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var animationData:AnimationData = animationDatas[i];
|
||||
objectName = animationData.objectName;
|
||||
objectData = objectDatas[objectName];
|
||||
if (objectData != null && objectData.vertices != null) {
|
||||
// Меш
|
||||
mesh = new Mesh(objectName);
|
||||
animationData.object = mesh;
|
||||
buildObject(animationData);
|
||||
buildMesh(mesh, objectData, animationData);
|
||||
} else {
|
||||
var object:Object3D = new Object3D(objectName);
|
||||
animationData.object = object;
|
||||
buildObject(animationData);
|
||||
}
|
||||
}
|
||||
buildHierarchy(_content, 0, length - 1);
|
||||
}
|
||||
} else {
|
||||
for (objectName in objectDatas) {
|
||||
objectData = objectDatas[objectName];
|
||||
if (objectData.vertices != null) {
|
||||
// Меш
|
||||
mesh = new Mesh(objectName);
|
||||
buildMesh(mesh, objectData, null);
|
||||
_content.addChild(mesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Рассылаем событие о завершении
|
||||
dispatchEvent(new Event(Event.COMPLETE));
|
||||
}
|
||||
|
||||
private function buildObject(animationData:AnimationData):void {
|
||||
var object:Object3D = animationData.object;
|
||||
if (animationData.position != null) {
|
||||
object.x = animationData.position.x;
|
||||
object.y = animationData.position.y;
|
||||
object.z = animationData.position.z;
|
||||
}
|
||||
if (animationData.rotation != null) {
|
||||
object.rotationX = animationData.rotation.x;
|
||||
object.rotationY = animationData.rotation.y;
|
||||
object.rotationZ = animationData.rotation.z;
|
||||
}
|
||||
if (animationData.scale != null) {
|
||||
object.scaleX = animationData.scale.x;
|
||||
object.scaleY = animationData.scale.y;
|
||||
object.scaleZ = animationData.scale.z;
|
||||
}
|
||||
object.mobility = mobility;
|
||||
}
|
||||
|
||||
private function buildMesh(mesh:Mesh, objectData:ObjectData, animationData:AnimationData):void {
|
||||
// Добавляем вершины
|
||||
var i:uint;
|
||||
var length:uint = objectData.vertices.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var vertexData:Point3D = objectData.vertices[i];
|
||||
objectData.vertices[i] = mesh.addVertex(vertexData.x, vertexData.y, vertexData.z, i);
|
||||
}
|
||||
|
||||
// Коррекция вершин
|
||||
if (animationData != null) {
|
||||
// Инвертируем матрицу
|
||||
objectData.matrix.invert();
|
||||
|
||||
// Вычитаем точку привязки из смещения матрицы
|
||||
objectData.matrix.d -= animationData.pivot.x;
|
||||
objectData.matrix.h -= animationData.pivot.y;
|
||||
objectData.matrix.l -= animationData.pivot.z;
|
||||
|
||||
// Трансформируем вершины
|
||||
for (var key:* in mesh._vertices) {
|
||||
var vertex:Vertex = mesh._vertices[key];
|
||||
vertex._coords.transform(objectData.matrix);
|
||||
vertex._coords.multiply(units);
|
||||
}
|
||||
}
|
||||
|
||||
// Добавляем грани
|
||||
length = objectData.faces.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
var faceData:FaceData = objectData.faces[i];
|
||||
mesh.addFace([objectData.vertices[faceData.a], objectData.vertices[faceData.b], objectData.vertices[faceData.c]], i);
|
||||
}
|
||||
// Добавляем поверхности
|
||||
if (objectData.surfaces != null) {
|
||||
for (var surfaceId:String in objectData.surfaces) {
|
||||
var materialData:MaterialData = materialDatas[surfaceId];
|
||||
var surfaceData:SurfaceData = objectData.surfaces[surfaceId];
|
||||
var surface:Surface = mesh.addSurface(surfaceData.faces, surfaceId);
|
||||
if (materialData.diffuseMap != null || materialData.normalMap != null) {
|
||||
surface.material = new TextureMaterial(new Texture(bitmaps[materialData.diffuseMap.filename], materialData.diffuseMap.filename), 1 - materialData.transparency/100, repeat, smooth, blendMode, precision);
|
||||
length = surfaceData.faces.length;
|
||||
if (objectData.uvs != null) {
|
||||
for (i = 0; i < length; i++) {
|
||||
var id:uint = surfaceData.faces[i];
|
||||
var fd:FaceData = objectData.faces[id];
|
||||
var face:Face = mesh.getFaceById(id);
|
||||
var aUV:Point = objectData.uvs[fd.a];
|
||||
var bUV:Point = objectData.uvs[fd.b];
|
||||
var cUV:Point = objectData.uvs[fd.c];
|
||||
if (aUV != null && bUV != null && cUV != null && ((bUV.x - aUV.x)*(cUV.y - aUV.y) - (bUV.y - aUV.y)*(cUV.x - aUV.x) != 0)) {
|
||||
face.aUV = materialData.matrix.transformPoint(aUV);
|
||||
face.bUV = materialData.matrix.transformPoint(bUV);
|
||||
face.cUV = materialData.matrix.transformPoint(cUV);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
surface.material = new FillMaterial(materialDatas[surfaceId].color, 1 - materialData.transparency/100);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Поверхность по умолчанию
|
||||
var defaultSurface:Surface = mesh.addSurface();
|
||||
// Добавляем грани
|
||||
for (var faceId:String in mesh._faces) {
|
||||
defaultSurface.addFace(mesh._faces[faceId]);
|
||||
}
|
||||
defaultSurface.material = new WireMaterial(0);
|
||||
}
|
||||
}
|
||||
|
||||
private function buildHierarchy(parent:Object3D, begin:uint, end:uint):void {
|
||||
if (begin <= end) {
|
||||
var animation:AnimationData = animationDatas[begin];
|
||||
var object:Object3D = animation.object;
|
||||
parent.addChild(object);
|
||||
|
||||
var parentIndex:uint = animation.parentIndex;
|
||||
for (var i:uint = begin + 1; i <= end; i++) {
|
||||
animation = animationDatas[i];
|
||||
if (parentIndex == animation.parentIndex) {
|
||||
buildHierarchy(object, begin + 1, i - 1);
|
||||
buildHierarchy(parent, i, end);
|
||||
return;
|
||||
}
|
||||
}
|
||||
buildHierarchy(object, begin + 1, end);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function parse3DSChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Главный
|
||||
case 0x4D4D:
|
||||
parseMainChunk(dataIndex, dataLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parse3DSChunk(index + chunkLength, length - chunkLength);
|
||||
} else {
|
||||
// Загрузка битмап
|
||||
loadBitmaps();
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMainChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Версия
|
||||
case 0x0002:
|
||||
parseVersion(dataIndex);
|
||||
break;
|
||||
// 3D-сцена
|
||||
case 0x3D3D:
|
||||
parse3DChunk(dataIndex, dataLength);
|
||||
break;
|
||||
// Анимация
|
||||
case 0xB000:
|
||||
parseAnimationChunk(dataIndex, dataLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parseMainChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseVersion(index:uint):void {
|
||||
data.position = index;
|
||||
version = data.readUnsignedInt();
|
||||
}
|
||||
|
||||
private function parse3DChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Материал
|
||||
case 0xAFFF:
|
||||
// Парсим материал
|
||||
var material:MaterialData = new MaterialData();
|
||||
parseMaterialChunk(material, dataIndex, dataLength);
|
||||
break;
|
||||
// Объект
|
||||
case 0x4000:
|
||||
// Создаём данные объекта
|
||||
var object:ObjectData = new ObjectData();
|
||||
var objectLength:uint = parseObject(object, dataIndex);
|
||||
// Парсим объект
|
||||
parseObjectChunk(object, dataIndex + objectLength, dataLength - objectLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parse3DChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMaterialChunk(material:MaterialData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Имя материала
|
||||
case 0xA000:
|
||||
parseMaterialName(material, dataIndex);
|
||||
break;
|
||||
// Ambient color
|
||||
case 0xA010:
|
||||
break;
|
||||
// Diffuse color
|
||||
case 0xA020:
|
||||
data.position = dataIndex + 6;
|
||||
material.color = ColorUtils.rgb(data.readUnsignedByte(), data.readUnsignedByte(), data.readUnsignedByte());
|
||||
break;
|
||||
// Specular color
|
||||
case 0xA030:
|
||||
break;
|
||||
// Shininess percent
|
||||
case 0xA040:
|
||||
data.position = dataIndex + 6;
|
||||
material.glossiness = data.readUnsignedShort();
|
||||
break;
|
||||
// Shininess strength percent
|
||||
case 0xA041:
|
||||
data.position = dataIndex + 6;
|
||||
material.specular = data.readUnsignedShort();
|
||||
break;
|
||||
// Transperensy
|
||||
case 0xA050:
|
||||
data.position = dataIndex + 6;
|
||||
material.transparency = data.readUnsignedShort();
|
||||
break;
|
||||
// Texture map 1
|
||||
case 0xA200:
|
||||
material.diffuseMap = new MapData();
|
||||
parseMapChunk(material.diffuseMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Texture map 2
|
||||
case 0xA33A:
|
||||
break;
|
||||
// Opacity map
|
||||
case 0xA210:
|
||||
break;
|
||||
// Bump map
|
||||
case 0xA230:
|
||||
material.normalMap = new MapData();
|
||||
parseMapChunk(material.normalMap, dataIndex, dataLength);
|
||||
break;
|
||||
// Shininess map
|
||||
case 0xA33C:
|
||||
break;
|
||||
// Specular map
|
||||
case 0xA204:
|
||||
break;
|
||||
// Self-illumination map
|
||||
case 0xA33D:
|
||||
break;
|
||||
// Reflection map
|
||||
case 0xA220:
|
||||
break;
|
||||
}
|
||||
|
||||
parseMaterialChunk(material, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMaterialName(material:MaterialData, index:uint):void {
|
||||
// Создаём список материалов, если надо
|
||||
if (materialDatas == null) {
|
||||
materialDatas = new Array();
|
||||
}
|
||||
// Получаем название материала
|
||||
material.name = getString(index);
|
||||
// Помещаем данные материала в список
|
||||
materialDatas[material.name] = material;
|
||||
}
|
||||
|
||||
private function parseMapChunk(map:MapData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Имя файла
|
||||
case 0xA300:
|
||||
map.filename = getString(dataIndex).toLowerCase();
|
||||
if (bitmaps == null) {
|
||||
bitmaps = new Array();
|
||||
}
|
||||
bitmaps[map.filename] = null;
|
||||
break;
|
||||
// Масштаб по U
|
||||
case 0xA354:
|
||||
data.position = dataIndex;
|
||||
map.scaleU = data.readFloat();
|
||||
break;
|
||||
// Масштаб по V
|
||||
case 0xA356:
|
||||
data.position = dataIndex;
|
||||
map.scaleV = data.readFloat();
|
||||
break;
|
||||
// Смещение по U
|
||||
case 0xA358:
|
||||
data.position = dataIndex;
|
||||
map.offsetU = data.readFloat();
|
||||
break;
|
||||
// Смещение по V
|
||||
case 0xA35A:
|
||||
data.position = dataIndex;
|
||||
map.offsetV = data.readFloat();
|
||||
break;
|
||||
// Угол поворота
|
||||
case 0xA35C:
|
||||
data.position = dataIndex;
|
||||
map.rotation = data.readFloat();
|
||||
break;
|
||||
}
|
||||
|
||||
parseMapChunk(map, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseObject(object:ObjectData, index:uint):uint {
|
||||
// Создаём список объектов, если надо
|
||||
if (objectDatas == null) {
|
||||
objectDatas = new Array();
|
||||
}
|
||||
// Получаем название объекта
|
||||
object.name = getString(index);
|
||||
// Помещаем данные объекта в список
|
||||
objectDatas[object.name] = object;
|
||||
return object.name.length + 1;
|
||||
}
|
||||
|
||||
private function parseObjectChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Меш
|
||||
case 0x4100:
|
||||
parseMeshChunk(object, dataIndex, dataLength);
|
||||
break;
|
||||
// Источник света
|
||||
case 0x4600:
|
||||
break;
|
||||
// Камера
|
||||
case 0x4700:
|
||||
break;
|
||||
}
|
||||
|
||||
parseObjectChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMeshChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Вершины
|
||||
case 0x4110:
|
||||
parseVertices(object, dataIndex);
|
||||
break;
|
||||
// UV
|
||||
case 0x4140:
|
||||
parseUVs(object, dataIndex);
|
||||
break;
|
||||
// Трансформация
|
||||
case 0x4160:
|
||||
parseMatrix(object, dataIndex);
|
||||
break;
|
||||
// Грани
|
||||
case 0x4120:
|
||||
var facesLength:uint = parseFaces(object, dataIndex);
|
||||
parseFacesChunk(object, dataIndex + facesLength, dataLength - facesLength);
|
||||
break;
|
||||
}
|
||||
|
||||
parseMeshChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseVertices(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.vertices = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
object.vertices.push(new Point3D(data.readFloat(), data.readFloat(), data.readFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
private function parseUVs(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.uvs = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
object.uvs.push(new Point(data.readFloat(), data.readFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
private function parseMatrix(object:ObjectData, index:uint):void {
|
||||
data.position = index;
|
||||
object.matrix = new Matrix3D();
|
||||
object.matrix.a = data.readFloat();
|
||||
object.matrix.e = data.readFloat();
|
||||
object.matrix.i = data.readFloat();
|
||||
object.matrix.b = data.readFloat();
|
||||
object.matrix.f = data.readFloat();
|
||||
object.matrix.j = data.readFloat();
|
||||
object.matrix.c = data.readFloat();
|
||||
object.matrix.g = data.readFloat();
|
||||
object.matrix.k = data.readFloat();
|
||||
object.matrix.d = data.readFloat();
|
||||
object.matrix.h = data.readFloat();
|
||||
object.matrix.l = data.readFloat();
|
||||
}
|
||||
|
||||
private function parseFaces(object:ObjectData, index:uint):uint {
|
||||
data.position = index;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
object.faces = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
var face:FaceData = new FaceData();
|
||||
face.a = data.readUnsignedShort();
|
||||
face.b = data.readUnsignedShort();
|
||||
face.c = data.readUnsignedShort();
|
||||
object.faces.push(face);
|
||||
data.position += 2; // Пропускаем флаг
|
||||
}
|
||||
return 2 + num*8;
|
||||
}
|
||||
|
||||
private function parseFacesChunk(object:ObjectData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
|
||||
switch (chunkId) {
|
||||
// Поверхности
|
||||
case 0x4130:
|
||||
parseSurface(object, dataIndex);
|
||||
break;
|
||||
// Группы сглаживания
|
||||
case 0x4150:
|
||||
break;
|
||||
}
|
||||
|
||||
parseFacesChunk(object, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseSurface(object:ObjectData, index:uint):void {
|
||||
// Создаём данные поверхности
|
||||
var surface:SurfaceData = new SurfaceData();
|
||||
// Создаём список поверхностей, если надо
|
||||
if (object.surfaces == null) {
|
||||
object.surfaces = new Array();
|
||||
}
|
||||
// Получаем название материала
|
||||
surface.materialName = getString(index);
|
||||
// Помещаем данные поверхности в список
|
||||
object.surfaces[surface.materialName] = surface;
|
||||
|
||||
// Получаем грани поверхности
|
||||
data.position = index + surface.materialName.length + 1;
|
||||
var num:uint = data.readUnsignedShort();
|
||||
surface.faces = new Array();
|
||||
for (var i:uint = 0; i < num; i++) {
|
||||
surface.faces.push(data.readUnsignedShort());
|
||||
}
|
||||
}
|
||||
|
||||
private function parseAnimationChunk(index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Анимация объекта
|
||||
case 0xB001:
|
||||
case 0xB002:
|
||||
case 0xB003:
|
||||
case 0xB004:
|
||||
case 0xB005:
|
||||
case 0xB006:
|
||||
case 0xB007:
|
||||
var animation:AnimationData = new AnimationData();
|
||||
if (animationDatas == null) {
|
||||
animationDatas = new Array();
|
||||
}
|
||||
animationDatas.push(animation);
|
||||
parseObjectAnimationChunk(animation, dataIndex, dataLength);
|
||||
break;
|
||||
|
||||
// Таймлайн
|
||||
case 0xB008:
|
||||
break;
|
||||
}
|
||||
|
||||
parseAnimationChunk(index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseObjectAnimationChunk(animation:AnimationData, index:uint, length:uint):void {
|
||||
if (length > 6) {
|
||||
data.position = index;
|
||||
var chunkId:uint = data.readUnsignedShort();
|
||||
var chunkLength:uint = data.readUnsignedInt();
|
||||
var dataIndex:uint = index + 6;
|
||||
var dataLength:uint = chunkLength - 6;
|
||||
switch (chunkId) {
|
||||
// Идентификация объекта и его связь
|
||||
case 0xB010:
|
||||
parseObjectAnimationInfo(animation, dataIndex);
|
||||
break;
|
||||
// Точка привязки объекта (pivot)
|
||||
case 0xB013:
|
||||
parseObjectAnimationPivot(animation, dataIndex);
|
||||
break;
|
||||
// Смещение объекта относительно родителя
|
||||
case 0xB020:
|
||||
parseObjectAnimationPosition(animation, dataIndex);
|
||||
break;
|
||||
// Поворот объекта относительно родителя (angle-axis)
|
||||
case 0xB021:
|
||||
parseObjectAnimationRotation(animation, dataIndex);
|
||||
break;
|
||||
// Масштабирование объекта относительно родителя
|
||||
case 0xB022:
|
||||
parseObjectAnimationScale(animation, dataIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
parseObjectAnimationChunk(animation, index + chunkLength, length - chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseObjectAnimationInfo(animation:AnimationData, index:uint):void {
|
||||
var name:String = getString(index);
|
||||
data.position = index + name.length + 1 + 4;
|
||||
animation.objectName = name;
|
||||
animation.parentIndex = data.readUnsignedShort();
|
||||
}
|
||||
|
||||
private function parseObjectAnimationPivot(animation:AnimationData, index:uint):void {
|
||||
data.position = index;
|
||||
animation.pivot = new Point3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
private function parseObjectAnimationPosition(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
animation.position = new Point3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
private function parseObjectAnimationRotation(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
animation.rotation = getRotationFrom3DSAngleAxis(data.readFloat(), data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
private function parseObjectAnimationScale(animation:AnimationData, index:uint):void {
|
||||
data.position = index + 20;
|
||||
animation.scale = new Point3D(data.readFloat(), data.readFloat(), data.readFloat());
|
||||
}
|
||||
|
||||
public function get content():Object3D {
|
||||
return _content;
|
||||
}
|
||||
|
||||
// Считываем строку заканчивающуюся на нулевой байт
|
||||
public function getString(index:uint):String {
|
||||
data.position = index;
|
||||
var charCode:uint = data.readByte();
|
||||
var res:String = "";
|
||||
while (charCode != 0) {
|
||||
res += String.fromCharCode(charCode);
|
||||
charCode = data.readByte();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private function getRotationFrom3DSAngleAxis(angle:Number, x:Number, z:Number, y:Number):Point3D {
|
||||
var res:Point3D = new Point3D();
|
||||
var s:Number = Math.sin(angle);
|
||||
var c:Number = Math.cos(angle);
|
||||
var t:Number = 1 - c;
|
||||
var k:Number = x*y*t + z*s;
|
||||
var half:Number;
|
||||
if (k > 0.998) {
|
||||
half = angle/2;
|
||||
res.z = -2*Math.atan2(x*Math.sin(half), Math.cos(half));
|
||||
res.y = -Math.PI/2;
|
||||
res.x = 0;
|
||||
return res;
|
||||
}
|
||||
if (k < -0.998) {
|
||||
half = angle/2;
|
||||
res.z = 2*Math.atan2(x*Math.sin(half), Math.cos(half));
|
||||
res.y = Math.PI/2;
|
||||
res.x = 0;
|
||||
return res;
|
||||
}
|
||||
res.z = -Math.atan2(y*s - x*z*t, 1 - (y*y + z*z)*t);
|
||||
res.y = -Math.asin(x*y*t + z*s);
|
||||
res.x = -Math.atan2(x*s - y*z*t, 1 - (x*x + z*z)*t);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
import alternativa.engine3d.core.Object3D;
|
||||
import alternativa.types.Matrix3D;
|
||||
import alternativa.types.Point3D;
|
||||
|
||||
import flash.geom.Matrix;
|
||||
|
||||
class MaterialData {
|
||||
public var name:String;
|
||||
public var color:uint;
|
||||
public var specular:uint;
|
||||
public var glossiness:uint;
|
||||
public var transparency:uint;
|
||||
public var diffuseMap:MapData;
|
||||
public var normalMap:MapData;
|
||||
public var matrix:Matrix;
|
||||
}
|
||||
|
||||
class MapData {
|
||||
public var filename:String;
|
||||
public var scaleU:Number = 1;
|
||||
public var scaleV:Number = 1;
|
||||
public var offsetU:Number = 0;
|
||||
public var offsetV:Number = 0;
|
||||
public var rotation:Number = 0;
|
||||
}
|
||||
|
||||
class ObjectData {
|
||||
public var name:String;
|
||||
public var vertices:Array;
|
||||
public var uvs:Array;
|
||||
public var matrix:Matrix3D;
|
||||
public var faces:Array;
|
||||
public var surfaces:Array;
|
||||
}
|
||||
|
||||
class FaceData {
|
||||
public var a:uint;
|
||||
public var b:uint;
|
||||
public var c:uint;
|
||||
}
|
||||
|
||||
class SurfaceData {
|
||||
public var materialName:String;
|
||||
public var faces:Array;
|
||||
}
|
||||
|
||||
class AnimationData {
|
||||
public var objectName:String;
|
||||
public var object:Object3D;
|
||||
public var parentIndex:uint;
|
||||
public var pivot:Point3D;
|
||||
public var position:Point3D;
|
||||
public var rotation:Point3D;
|
||||
public var scale:Point3D;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 100
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/materials
|
||||
END
|
||||
Material.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 112
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/materials/Material.as
|
||||
END
|
||||
TextureMaterial.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 119
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/materials/TextureMaterial.as
|
||||
END
|
||||
FillMaterial.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 116
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/materials/FillMaterial.as
|
||||
END
|
||||
WireMaterial.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 116
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/materials/WireMaterial.as
|
||||
END
|
||||
DrawPoint.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 113
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/materials/DrawPoint.as
|
||||
END
|
||||
SurfaceMaterial.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 119
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/materials/SurfaceMaterial.as
|
||||
END
|
||||
TextureMaterialPrecision.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 128
|
||||
/!svn/ver/162/platform/clients/fp9/libraries/Alternativa3D/tags/5.0.0/alternativa/engine3d/materials/TextureMaterialPrecision.as
|
||||
END
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user