more versions added

This commit is contained in:
Tubix
2024-10-05 12:11:16 +01:00
parent 413f563f33
commit c32c7e8c34
7661 changed files with 1343635 additions and 0 deletions

BIN
Alternativa3D5/.DS_Store vendored Normal file

Binary file not shown.

View 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>

View 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>

View 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>

View 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

View 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

View File

@@ -0,0 +1 @@
8

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,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

View 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

View File

@@ -0,0 +1 @@
8

View File

@@ -0,0 +1,5 @@
K 13
svn:mime-type
V 24
application/octet-stream
END

View 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>

View 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>

View 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>

View 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.

View 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. В максимальной степени, допустимой действующим законодательством,
Правообладатель, равно как и его партнеры, не несет никакой ответственности
за какие-либо прямые или косвенные последствия какого-либо использования или
невозможности использования Программы и/или ущерб, причиненный Пользователю
и/или третьим сторонам в результате какого-либо использования или
неиспользования Программы, в том числе из-за возможных ошибок или сбоев в работе
Программы.

View 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

View 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

View File

@@ -0,0 +1 @@
8

View 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

View 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

View File

@@ -0,0 +1 @@
8

View File

@@ -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;
}
}

View File

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

View File

@@ -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;
}
}

View File

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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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

View 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

View File

@@ -0,0 +1 @@
8

View 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);
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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());
}
}
}
}

View 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;
}
}
}

View 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 + "]";
}
}
}

View File

@@ -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

View 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;
/**
* Поверхность &mdash; набор граней, объединённых в группу для удобства работы с их общими свойствами.
*/
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;
}
}
}

View 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) + "]";
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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());
}
}
}
}

View 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;
}
}
}

View 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 + "]";
}
}
}

View File

@@ -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

View 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;
/**
* Поверхность &mdash; набор граней, объединённых в группу для удобства работы с их общими свойствами.
*/
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;
}
}
}

View 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) + "]";
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1 @@
8

View 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);
}
}
}

View 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);
}
}
}
}
}

View 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);
}
}
}

View 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);
}
}
}
}
}

View File

@@ -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

View 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

View File

@@ -0,0 +1 @@
8

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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";
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1 @@
8

View 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;
}

View 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;
}

View File

@@ -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